简介#
在无人机编队仿真的第三部分,我们聚焦于将目标识别技术集成到 ROS2+PX4 仿真环境 中,利用YOLOv8实现高效的实时目标检测。YOLO(You Only Look Once)是一种高效、实时的目标检测算法,广泛应用于计算机视觉任务,如无人机目标识别、自动驾驶和监控。
我们基于PX4 SITL(软件在环仿真)和Gazebo Ignition,结合gz_x500_depth四旋翼模型,展示如何通过ROS2订阅无人机搭载的RGB相机(OakD-Lite)图像,应用YOLOv8模型检测特定目标,并在Ubuntu 22.04环境中实时可视化检测结果。这可以为后续编队协同任务提供更多技术支持。
创建Python虚拟环境#
我们使用的Python版本号为3.10.12,读者可以根据自己的Python版本更改命令。
# 创建虚拟环境
python3 -m venv ~/px4-venv
# 激活
source ~/px4-venv/bin/activate
# 链接ROS2环境
source /opt/ros/humble/setup.bash
export PYTHONPATH=/opt/ros/humble/lib/python3.10/site-packages:$PYTHONPATHbash安装MAVSDK#
如果后续出现Numpy的版本号冲突的问题,建议先使用 pip uninstall numpy -y 卸载当前版本,再按照以下命令安装Numpy 1.x版本。
pip install mavsdk
pip install aioconsole
pip install pygame
sudo apt install ros-humble-ros-gzgarden
pip install "numpy<2"
pip install opencv-pythonbash安装YOLO#
pip install ultralyticsbash启动带相机的四旋翼模型并添加相机话题#
我们稍微修改一下前两文的启动仿真命令:
PX4_GZ_STANDALONE=1 PX4_SYS_AUTOSTART=4001 PX4_GZ_MODEL_POSE="0,0" PX4_SIM_MODEL=gz_x500_depth ./build/px4_sitl_default/bin/px4 -i 0bash我们再来看一下这个启动命令,对于x500模型,4001/4002分别对应两种不同的四旋翼配置(“X”型和十字型机架),在此处,我们使用带深度相机的 gz_x500_depth 的x_500模型,这与之前的有所不同。
我们前往models所在的文件夹,找到 x500_depth 的 model.sdf 文件以及其相机模块 OakD_Lite 的sdf文件:
我们发现,相机模块OakD_Lite一共搭载了两个相机传感器,分别为IMX214(RGB相机)和StereoOV7251(深度相机)。其中RGB相机的输出图像格式为RGB_INT8,深度相机的输出图像格式为R_FLOAT32。RGB相机是常用的进行图像识别的传感器,而深度相机用于探测距离,在避障任务中有很好的应用。在此处,我们先使用RGB相机。
现在我们需要关注sdf文件的这几行:
<width>1920</width>
<height>1080</height>
<format>RGB_INT8</format>xml这三个是比较重要的参数,代表图像的尺寸和格式,这在后续的代码中需要注意,尤其是图像的格式需要匹配,RGB_INT8是一个三通道的参数。
<always_on>1</always_on>
<update_rate>30</update_rate>
<visualize>true</visualize>
<topic>image_raw</topic>xml<always_on> 代表相机持续保持开启状态,我们在最后为这个相机添加一个ROS2话题(此处为笔者自行添加),名称为 image_raw,后续我们会利用这个话题进行消息的传输。
调用YOLOv8进行目标识别#
mkdir -p ~/YOLO
cd ~/YOLO
touch uav_camera_det.pybash创建一个/YOLO目录,现在编写一个调用YOLOv8进行目标检测的Python代码。
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
from ultralytics import YOLO
model = YOLO('yolov8m.pt')python调用一些需要的库和图像转换工具,类似于上一篇文章,rclpy是ROS2的Python客户端,CvBridge是ROS与openCV的图像转换工具,yolov8m.pt 是中等规模模型(Medium),基于COCO数据集训练,支持80类目标。以下是COCO数据集的80个完整类别:
0: person 1: bicycle 2: car 3: motorcycle
4: airplane 5: bus 6: train 7: truck
8: boat 9: traffic light 10: fire hydrant 11: stop sign
12: parking meter 13: bench 14: bird 15: cat
16: dog 17: horse 18: sheep 19: cow
20: elephant 21: bear 22: zebra 23: giraffe
24: backpack 25: umbrella 26: handbag 27: tie
28: suitcase 29: frisbee 30: skis 31: snowboard
32: sports ball 33: kite 34: baseball bat 35: baseball glove
36: skateboard 37: surfboard 38: tennis racket 39: bottle
40: wine glass 41: cup 42: fork 43: knife
44: spoon 45: bowl 46: banana 47: apple
48: sandwich 49: orange 50: broccoli 51: carrot
52: hot dog 53: pizza 54: donut 55: cake
56: chair 57: couch 58: potted plant 59: bed
60: dining table 61: toilet 62: tv 63: laptop
64: mouse 65: remote 66: keyboard 67: cell phone
68: microwave 69: oven 70: toaster 71: sink
72: refrigerator 73: book 74: clock 75: vase
76: scissors 77: teddy bear 78: hair drier 79: toothbrushplaintext为了减少计算消耗,提高检测速度,我们往往将检测目标进行缩小,我们稍后讲。
class ImageSubscriber(Node):
def __init__(self):
super().__init__('image_subscriber')
self.subscription = self.create_subscription(
Image,
'/image_raw',
self.listener_callback,
10)
self.br = CvBridge()
self.frame_count = 0
self.process_interval = 1
cv2.namedWindow('YOLOv8 Detection', cv2.WINDOW_NORMAL)
cv2.resizeWindow('YOLOv8 Detection', 640, 480)python类似于上一篇文章使用C++编写的Offboard指令,这里我们定义一个新的类,发布一个名为 image_subscriber 的ROS2订阅者实例,进行初始化。我们在这里需要注意,在创建订阅者实例时,话题名称要与我们之前修改的RGB相机的sdf文件的话题名称相同,这里为我们之前发布的 /image_raw,需要匹配。调用CvBridge函数,这是因为我们需要将ROS的图像格式(RGB8)转换为OpenCV的图像格式(BGR8),最后将窗口可视化,以640×480的尺寸进行可视化。
def listener_callback(self, data):
self.frame_count += 1
if self.frame_count % self.process_interval != 0:
self.get_logger().info("跳过帧")
return
try:
self.get_logger().info(f"收到视频帧,编码: {data.encoding}")
image = self.br.imgmsg_to_cv2(data, desired_encoding="bgr8")
self.get_logger().info(f"图像尺寸: {image.shape}")
image = cv2.resize(image, (640, 480))
cv2.imwrite("input_image.jpg", image)
results = model.predict(image, classes=[0, 2])
annotated_image = results[0].plot()
cv2.imwrite("yolo_output.jpg", annotated_image)
cv2.imshow('YOLOv8 Detection', annotated_image)
if cv2.waitKey(1) & 0xFF == ord('q'):
raise KeyboardInterrupt
self.get_logger().info(f"检测到 {len(results[0].boxes)} 个目标")
for box in results[0].boxes:
self.get_logger().info(f"检测到: 类别={box.cls}, 置信度={box.conf}, 坐标={box.xyxy}")
except Exception as e:
self.get_logger().error(f"处理图像错误: {str(e)}")python定义回调函数,image = cv2.resize(image, (640, 480)) 是用于图像的预处理的函数,将我们的sdf里定义的1920×1080的图像压缩为640×480。随后使用 results = model.predict(image, classes=[0, 2]) 运行YOLOv8,限制检测类别为0,2(对应人、车),这样可以提高检测的速度。如果要检测其他物体,可以按照上述的80个类别编号进行添加。如果80个类别都要检测,可以使用 results = model.predict(image) 实现全部检测,最后进行可视化,但这样肯定会极大地浪费计算效率。
def destroy_node(self):
cv2.destroyAllWindows()
super().destroy_node()python重写一个销毁ROS2节点的方法,关闭OpenCV的窗口,清理ROS2节点和环境。
def main(args=None):
rclpy.init(args=args)
image_subscriber = ImageSubscriber()
try:
rclpy.spin(image_subscriber)
except KeyboardInterrupt:
pass
finally:
image_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()python最后执行 main() 函数,实现对话题的订阅以及调用YOLOv8实现无人机的目标识别及检测。至此,我们进行ROS2+PX4+YOLOv8的目标识别任务的准备就做好了。
实现仿真#
# 终端1
python3 simulation-gazebo
# 终端2
cd ~/PX4-Autopilot
PX4_GZ_STANDALONE=1 PX4_SYS_AUTOSTART=4001 PX4_GZ_MODEL_POSE="0,0" PX4_SIM_MODEL=gz_x500_depth ./build/px4_sitl_default/bin/px4 -i 0
# 终端3
MicroXRCEAgent udp4 -p 8888bash按照之前的步骤启动Gazebo仿真、带深度相机的四旋翼模型以及打开通信,现在我们需要运行这个命令:
ros2 run ros_gz_image image_bridge /image_rawbash这是一个将ROS2与Gazebo进行桥接的命令,可以将Gazebo里的话题消息转换为ROS2的 sensor_msgs.msg.Image 消息,并发布到ROS2话题。此时我们订阅的就是我们之前在sdf文件里添加的 /image_raw 话题。
运行这个命令后,我们执行一下我们的YOLOv8检测的代码,并且运行一下我们的Offboard控制,让飞机飞起来,在可视化窗口查看一下摄像头和检测信息。
# 终端1 — 激活虚拟环境并运行YOLO检测
source ~/px4-venv/bin/activate
cd ~/YOLO
python3 uav_camera_det.py
# 终端2 — 运行Offboard控制
ros2 run multioffboardcontrol uav0_1bash结果展示#
视频 1:成功启动YOLO检测
我们发现,带深度相机的四旋翼飞机成功启动仿真,并且YOLOv8成功启动了检测。由于笔者的世界搭建里还没有加入车和人,所以没有目标探测到,读者朋友们可以进行添加并验证。
同时,由于相机是固连在机身坐标系上,而作者此时的Offboard模式使用的是盘旋指令,因此相机会跟着飞机姿态进行旋转,图像不稳定。后续可以采取平飞+调整相机姿态的方式实现更稳定的目标识别。
总结#
在这篇文章里,我们通过调用YOLOv8实现了在ROS2+PX4中进行目标检测的任务。需要着重注意的是相机种类(RGB相机和深度相机)的区别,同时注意sdf文件里是否有相应的话题配置,如果没有,需要手动添加。在仿真过程中我们发现还有一些细节可以调整,例如如何适应相机随着机身坐标系的偏转,如何提高识别成功率等,读者朋友们可以在实际应用场景中进行改进。
在后续的任务中,搭配RGB相机和深度相机的四旋翼飞机可以实现场景下的目标识别、自主避障(深度相机)等任务。如果需要在多机编队中集成视觉感知,可以参考前文的 ROS2+PX4 多机 Offboard 控制,将目标检测与编队飞行结合。期待大家的实际应用。
最后,再次感谢读者朋友们的支持和耐心的阅读,欢迎大家对本文进行批评指正、补充及建议。