【有奖征文】基于Intel哪吒开发板的ROS巡航机器人图像推理设计

openlab_96bf3613 更新于 1月前

有奖征文详情
原文链接:【有奖征文】基于Intel哪吒开发板的ROS巡航机器人图像推理设计_IVEN1799-英特尔开发套件专区

一、项目概述
随着科技的飞速发展,巡逻机器人作为智能化、自动化的代表,正在逐渐改变传统的安防模式。该项目通过搭载高清相机,实时采集环境图像,并利用先进的 BLIP 大模型进行图像分析,从而识别异常事件或目标物体。本项目旨在开发一个基于 ROS2 框架的智能巡航机器人系统,该系统能够在 Intel 哪吒开发板上高效运行图像推理任务。

Intel 哪吒开发板采用高性能的 Intel N97 处理器(Alder Lake-N),具备四核 SoC,主频高达 3.60GHz,并内置 Intel UHD Graphics Gen12 GPU,支持多达 24 个执行单元,适用于 AI 推理任务。开发板尺寸为 85mm x 56mm,配备 8GB LPDDR5 内存和 64GB eMMC 存储,内置 TPM 2.0 模块和 40 针 GPIO 连接器,提供丰富的外设接口,非常适合嵌入式机器人应用。

ROS2(Robot Operating System 2)采用了 DDS(Data Distribution Service,数据分布服务)作为其底层通信机制的核心部分。DDS 是一种面向实时系统的开放标准,特别适合于构建大规模分布式系统。ROS2 通过 DDS 提供了高效的系统集成能力和实时通信支持。

BLIP(Bridging Language and Perception)是一个多模态预训练模型,专门用于连接语言和视觉信息处理。它是一个强大的多模态预训练模型,能够处理诸如图像描述生成、视觉问答等多种任务。该模型由三部分组成:

Vision Model:图像表示的编码器,负责提取图像特征。
Text Encoder:输入查询的编码器,主要用于处理视觉问答和文本到图像检索任务中的文本输入。
Text Decoder:输出答案的解码器,负责生成最终的答案或描述。
通过使用 OpenVINO(Open Visual Inference & Neural Network Optimization)工具套件,可以直接将 PyTorch 模型转换为 OpenVINO 中间表示(IR)格式。这一转换允许模型在 Intel 哪吒开发板上加速推理过程,从而显著提升处理速度。这使得在边缘设备上运行复杂的视觉任务成为可能,同时保持高性能和低延迟,非常适合实时机器人应用。

在 ROS2 的环境中,BLIP 可以被用来处理机器人感知系统中的视觉语言任务。例如,在一个机器人导航任务中,BLIP 可以用于理解环境中的图像,并生成相应的描述或回答相关问题。通过结合 ROS2 的强大通信能力,BLIP 模型可以在不同节点之间高效地交换信息,从而实现复杂的机器人行为。

使用 OpenVINO 对 BLIP 模型进行优化和加速,不仅可以提高模型的运行效率,还可以降低功耗,这对于在资源受限的嵌入式平台上部署机器学习模型尤为重要。这样可以确保机器人系统能够快速响应环境变化,同时保持系统的稳定性和可靠性。

二、系统设计
1、硬件平台

英特尔开发者套件-哪吒是一款高性能AI开发板,搭载Intel N97处理器,配备8GB LPDDR5内存和64GB eMMC存储空间,支持Windows和Linux操作系统,适用于自动化、物联网**、数字标牌和机器人等场景。它具有高性能与低功耗特性,内置Intel UHD Graphics Gen12 GPU,支持高分辨率显示,并提供丰富的外设接口。此外,哪吒开发套件目前暂不支持蓝牙和WiFi连接,只能通过外置网卡的方案解决某些对无线连接需求较高的应用场景。如图1所示。
外置WIFI网卡,如rtl18821cu的驱动程序,可下载并进行安****r/>

git clone https://github.com/brektrou/rtl8821CU.git
cd rtl8821CU/
sudo ./dkms-install.sh

Intel GPU驱动,安装哪吒开发板的驱动程序和必要的软件包,可额外安装来提高推理速度。

sudo apt update
sudo apt install -y gpg-agent wget
wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | sudo gpg --yes --dearmor --output /usr/share/keyrings/intel-graphics.gpg
echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy client" | sudo tee /etc/apt/sources.list.d/intel-gpu-jammy.list
sudo apt update
sudo apt install -y intel-opencl-icd intel-level-zero-gpu level-zero


图1 英特尔-哪吒开发者套介绍

2、环境搭建
2.1 Ubuntu 22.04 LTS
下载链接:Ubuntu 22.04 LTS
2.2 安装ROS2 Humble
更新系统包列表与编码设置 :

sudo apt update && sudo apt upgrade -y
sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

添加ROS2仓库:

sudo apt install curl gnupg l**-release
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

安装ROS2基础包:

sudo apt update
sudo apt upgrade
sudo apt install ros-humble-desktop

环境配置:

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc # 所有终端均生效
source ~/.bashrc

2.3 安装Miniconda

# 下载 Miniconda 安装脚本
wget https://repo.anaconda.com/miniconda/Miniconda3-py310_24.7.1-0-Linux-x86_64.sh
# 运行 Miniconda 安装脚本
bash Miniconda3-py310_24.7.1-0-Linux-x86_64.sh
# 初次安装需要激活 base 环境
source ~/.bashrc
# 改换国内源
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkg***ain/
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/
conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
conda config --set show_channel_urls yes

(2)创建并激活ros2虚拟环境

conda create -n ros2 python=3.10
echo "conda activate ros2" >> ~/.bashrc
source ~/.bashrc


2.4 安装OpenVINO 2024.3.0
下载链接:OpenVINO 2024.3.0

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/
pip install openvino-genai==2024.3.0

2.5 下载BLIP Model
下载链接:BLIP Image Captioning Large Model

pip install -U huggingface_hub
echo "export HF_ENDPOINT=https://hf-mirror.com" >> ~/.bashrc
source ~/.bashrc
pip install git-lfs
git clone https://hf-mirror.com/Salesforce/blip-image-captioning-large

安装BLIP Model依赖

pip install torch>=2.1.0 torchvision transformers>=4.26.0 gradio>=4.19
pip install datasets>=2.14.6 nncf>=2.8.1 tqdm matplotlib==3.6
pip install timm==0.4.12 fairscale==0.4.4

三、ROS2节点设计
1. 创建ROS2工作空间
首先,创建一个ROS2工作空间,并使用rosdep工具自动安装相关依赖。

mkdir -p ~/intel_nezha_ws/src/
cd ~/intel_nezha_ws/src
sudo apt install -y python3-pip
sudo pip3 install rosdepc
sudo rosdepc init
rosdepc update
cd ..
rosdepc install -i --from-path src --rosdistro humble -y

2. 编译工作空间
依赖安装完成后,需要下载colcon对工作空间进行编译。

sudo apt-get install python3-colcon-common-extensions
cd ~/ros2_nazha_ws/
colcon build

3. 设置环境变量
编译成功后,为了让系统能够找到我们的功能包和可执行文件,还需要设置环境变量:

source install/local_setup.sh # 仅在当前终端生效
echo " source ~/intel_nezha_ws/install/local_setup.sh" >> ~/.bashrc # 所有终端均生效

4. 创建客户端/服务器模型的ROS2功能包以及服务接口。

cd src
ros2 pkg create nintel --build-type ament_python
ros2 pkg create nezha_ros2_interface*****uild-type ament_cmake --dependencies rosidl_default_generators

5. 创建接口定义文件

sudo vim ~/ros2_nezha_ws/nezha_ros2_interfaces/srv/ImageInference.srv
bool get
---
String s

6. USB-CAM节点实现
将USB相机与哪吒开发板连接,并安装必要的驱动程序。可通过 以下指令查看当前设备连接状况。

lsu**

这将列出所有连接的USB设备。找到相机相对应的条目。常用的u**相机驱动一般都是通用的,ROS中也集成了u**相机的标准驱动,可以直接使用ROS中的相机驱动节点。

7. 安装u**-cam相机驱动

sudo apt install ros-humble-u**-cam

如果摄像头被正确识别,它应该会被自动加载相应的驱动。可以通过l**od命令来检查是否已加载了相关驱动:

l**od | grep uvc

为了确保摄像头能够正常工作,需要安装一些基本的视频捕获工具。例如,v4l-utils和ffmpeg等工具可以帮助您测试摄像头:

sudo apt install -y v4l-utils ffmpeg
v4l2-ctl --list-devices
ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -preset ultrafast -f mpegts pipe:1 > video.mpg

在ROS2中创建一个相机驱动节点,该节点负责从相机获取图像数据,并将其作为ROS2消息发布。

为了确保您的摄像头能够正常工作,需要安装ROS2的图像传输工具包:

sudo apt install -y ros-humble-image-transport-plugins

8. 运行u**相机图像采集节点

ros2 run u**_cam u**_cam_node_exe

9. 服务端节点实现
主要功能:订阅USB图像数据,接收客户端图像分析请求,通过openvino实现对blip大模型进行推理,提高推理速度。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# service_object_server.py

import json
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from sensor_msg***sg import Image # 图像消息类型
import numpy as np # Python数值计算库
from cv_bridge import CvBridge # ROS与OpenCV图像转换类
import cv2 # OpenCV图像处理库
from nezha_ros2_interface.srv import GetObjectInformation # 自定义的服务接口
import sys
sys.path.append('/home/iven/miniconda3/envs/ros2/lib/python3.10/site-packages/') # 添加依赖库路径
sys.path.append('src/blip-vqa-base/') # 添加项目源码路径
from transformers import BlipProcessor, BlipForConditionalGeneration, BlipConfig
import openvino as ov
from pathlib import Path
from blip_model import OVBlipModel, text_decoder_forward
from functools import partial
from utils import visualize_results


class ImageSubscriber(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.sub = self.create_subscription(
Image, 'image_raw', self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
self.cv_bridge = CvBridge() # 创建一个图像转换对象,用于OpenCV图像与ROS的图像消息的互相转换

self.srv = self.create_service(GetObjectInformation, # 创建服务器对象(接口类型、服务名、服务器回调函数)
'get_target_information',
self.object_information_callback)

self.image = None # 用于存储接收到的图像

# 加载配置文件
config_path = "src/blip-vqa-base/config.json"
self.config = BlipConfig.from_pretrained(config_path) # 从预训练配置文件加载配置

# 打印配置信息,确保其包含所需的属性
self.get_logger().info(f"Loaded Config: {self.config.to_json_string()}") # 打印配置信息
if not hasattr(self.config.text_config, 'decoder_start_token_id'):
# 如果text_config中没有设置decoder_start_token_id,
# 尝试从vision_config中获取
if hasattr(self.config.vision_config, 'decoder_start_token_id'):
self.config.text_config.decoder_start_token_id = self.config.vision_config.decoder_start_token_id
else:
# 如果vision_config中也没有设置,则手动设置一个默认值
self.config.text_config.decoder_start_token_id = 31884 # 替换为实际值

# 获取正确的decoder_start_token_id
self.decoder_start_token_id = self.config.text_config.decoder_start_token_id
self.get_logger().info(f"Loaded Decoder start token ID: {self.decoder_start_token_id}")

# 加载模型
model_path = "src/blip-vqa-base"
self.model = BlipForConditionalGeneration.from_pretrained(model_path, config=self.config) # 从预训练模型加载模型
self.processor = BlipProcessor.from_pretrained(model_path) # 从预训练模型加载处理器

# 初始化OVBlipModel
self.initialize_ov_model()

def initialize_ov_model(self):
# 加载OpenVINO IR模型
VISION_MODEL_OV = Path("src/blip-vqa-base/blip_vision_model.xml")
TEXT_ENCODE_OV = Path("src/blip-vqa-base/blip_text_encoder.xml")
TEXT_DECODE_OV = Path("src/blip-vqa-base/blip_text_decoder_with_past.xml")
self.get_logger().info(f"Loading models from paths: {VISION_MODEL_OV}, {TEXT_ENCODE_OV}, {TEXT_DECODE_OV}") # 日志记录
core = ov.Core()
try:
ov_vision_model = core.compile_model(model=VISION_MODEL_OV, device_name="GPU") # 编译视觉模型
ov_text_encoder = core.compile_model(model=TEXT_ENCODE_OV, device_name="GPU") # 编译文本编码器
ov_text_decoder_with_past = core.compile_model(model=TEXT_DECODE_OV, device_name="GPU") # 编译带过去状态的文本解码器
self.get_logger().info(f"Models loaded successfully. decoder_start_token_id: {self.decoder_start_token_id}") # 日志记录
text_decoder = self.model.text_decoder
text_decoder.eval() # 设置评估模式
text_decoder.forward = partial(text_decoder_forward, ov_text_decoder_with_past=ov_text_decoder_with_past) # 替换forward方法
self.ov_model = OVBlipModel(self.config, self.decoder_start_token_id, ov_vision_model, ov_text_encoder, text_decoder) # 初始化OVBlipModel
except Exception as e:
self.get_logger().error(f"Failed to load models: {e}") # 日志记录失败信息

def object_detect(self, image):
self.image = image # 存储接收到的图像
cv2.imshow("object", image) # 使用OpenCV显示处理后的图像效果
cv2.waitKey(50) # 等待一段时间以显示图像

def listener_callback(self, data):
self.get_logger().info('Receiving video frame') # 输出日志信息,提示已进入回调函数
image = self.cv_bridge.imgmsg_to_cv2(data, 'bgr8') # 将图像消息转化成OpenCV图像
self.image = image # 保存图像以便后续使用
self.object_detect(image) # 调用对象检测方法

def openVino(self):
text = "a photography of"
inputs = self.processor(images=self.image, text=text, return_tensors="pt") # 处理图像和文本输入

# 检查是否包含所需参数
assert 'input_ids' in inputs, "'input_ids' i***issing"
assert 'attention_mask' in inputs, "'attention_mask' i***issing"

input_ids = inputs.input_ids
pixel_values = inputs.pixel_values
attention_mask = inputs.attention_mask

# 生成答案
out = self.ov_model.generate_answer(pixel_values=pixel_values, input_ids=input_ids, attention_mask=attention_mask, max_length=1024)
answer = self.processor.decode(out[0], skip_special_tokens=True) # 解码输出结果
return answer

def object_information_callback(self, request, response):
if self.image is None:
self.get_logger().warn('No image received yet.') # 输出日志信息,提示尚未接收到图像
return response

if request.get:
response.s = self.openVino() # 调用openVino方法获取结果
self.get_logger().info('Object position\ns:%s' % (response.s)) # 输出日志信息,提示已经反馈
else:
response.s = "null" # 请求无效
self.get_logger().info('Invalid command') # 输出日志信息,提示请求无效
return response

def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = ImageSubscriber("service_object_server") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口

if __name__ == '__main__':
main() # 主程序入口


    
主要功能:订阅图像采集节点数据,向服务端节点提交推理请求,并接收图像描述反馈。

# ~/ros2_nezha_ws/nezha_ros2_service/llm_client.py
import rclpy
from rclpy.node import Node
from inference_service.srv import ImageInference
from sensor_msg***sg import Image
from cv_bridge import CvBridge
import cv2
import numpy as np

class InferenceClient(Node):
def __init__(self):
super().__init__('llm_client')
self.sub =self.create_subscription(Image,'image_raw',self.listener_callback,10)
self.client = self.create_client(ImageInference, 'image_inference')
while not self.client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('服务未准备好,正在等待...')

# 创建cv_bridge对象,用于图像消息与OpenCV图像之间的转换
self.bridge = CvBridge()

def send_request(self, image_path):
# 加载图像
img = cv2.imread(image_path)
# 将图像转换为ROS消息
img_msg = self.bridge.cv2_to_imgmsg(img, encoding='rgb8')

# 创建请求对象
req = ImageInference.Request()
req.data = img_msg

# 异步发送请求
self.future = self.client.call_async(req)

def main(args=None):
rclpy.init(args=args)
inference_client = InferenceClient()

# 替换此行为获取图像消息的实际代码
image_path = "/path/to/your/image.jpg" # 图像路径
inference_client.send_request(image_path)

# 主循环
while rclpy.ok():
rclpy.spin_once(inference_client)
# 检查是否完成
if inference_client.future.done():
try:
# 获取结果
response = inference_client.future.result()
except Exception as e:
# 处理异常
inference_client.get_logger().info('服务调用失败:%r' % (e,))
else:
# 输出结果
inference_client.get_logger().info('推理结果:%s' % (response.result,))
break
inference_client.destroy_node()
rclpy.shutdown()

if __name__ == '__main__':
main()


11. 编译和运行
运行摄像头图像采集节点、服务端和客户端节点。

#编译整个工作空间

colcon build
source install/local_setup.bash
# 运行服务端节点
ros2 run nezha_ros2_service llm_server

# 在另一个终端窗口运行客户端节点
ros2 run nezha_ros2_service llm_client


服务节点运行结果如图2所示,客户节点运行结果如图3所示。

    
图2 服务节点视频采集图像


图3客户节点请求BLIP分析结果

四、总结

以上是实现基于ROS2和blip大模型的巡逻机器人,采用blip-vqa-base模型实现图像分析方面的一个方案,该模型只能简单识别物体,考虑到哪吒开发板作为巡逻机器人的控制板,主要负责巡逻机器人的运动控制以及各种传感器的数据传输。因此,大模型不宜安装在哪吒开发板上,可考虑升级带有GPU的开发板,如算力魔方。或者把大模型安装在带有ROS2系统的边缘aipc/服务器上,通过ROS节点进行DDS通信。同时需要更换其它模型来优化识别物体的能力。该系统适用于工业自动化中的质量控制、智能仓库中的物品识别以及公共区域的安全监控等场景,能够提供高效、可靠的解决方案。

0个评论