基于S100(P)的语音-LLM交互--实现思路分享

语音-LLM交互通路实现

本示例测试于RDK S100 & S100P平台,外设接线如下图所示。


※LLM(1.5B)演示效果预览※

从语音指令输入结束开始,LLM语音首字响应达到1.5s


1、设备确认与测试

参考来源:3.2.1 RDK S100 系列音频使用指南 | RDK DOC

  1. 确认设备编号
### 1、确认设备编号
arecord -l
aplay -l
# 检查硬件支持能力 1,0对应卡号和设备号
arecord --dump-hw-params -Dhw:1,0
aplay --dump-hw-params -Dhw:1,0 /dev/zero

  1. 本地音频收放测试
### 2、录音
arecord -Dhw:1,0 -c 1 -r 16000 -f S16_LE -t wav -d 5 test.wav
# 音频转换
ffmpeg -i test.wav -ac 2 -ar 48000 test_c2.wav
# 音频播放
aplay -Dhw:1,0 test_c2.wav

2、算法包安装:

sudo apt update
# ROS2编译工具安装
sudo apt install python3-colcon-common-extensions
# 安装算法包
sudo apt install tros-humble-sensevoice-ros2\
 tros-humble-hobot-tts \
 tros-humble-hobot-xlm

3、单步测试

3.1 文本转语音(TTS)

参考来源:5.2.8 文本转语音 | RDK DOC

通过TTS模型实现文本转语音功能。

# 安装tts_model并解压到tros下
wget http://archive.d-robotics.cc/tts-model/tts_model.tar.gz
sudo tar -xf tts_model.tar.gz -C /opt/tros/${TROS_DISTRO}/lib/hobot_tts/
  1. 启动TTS节点
source /opt/tros/humble/setup.bash
# 屏蔽调式打印信息
export GLOG_minloglevel=1
# 启动,配置对应的设备号
ros2 run hobot_tts hobot_tts --ros-args -p playback_device:="plughw:1,0"
  1. 测试,手动发布话题进行测试,发布话题后音频设备应有声音输出。
ros2 topic pub --once /tts_text std_msgs/msg/String "{data: "" 你好,我是地瓜机器人小助手""}"

3.2 语音转文本(ASR)

参考来源:Sensevoice | RDK DOC

通过ASR的.gguf模型实现语音转文本功能。

模型路径:/opt/tros/humble/lib/sensevoice_ros2/model/

  1. 启动ASR节点
source /opt/tros/humble/setup.bash
ros2 run sensevoice_ros2 sensevoice_ros2 --ros-args -p push_wakeup:=0 \
    -p asr_pub_topic_name:=/prompt_text \
    -p micphone_name:="plughw:1,0"

向音频设备说话后,输出示例如下:

※※※需要注意的是,文本话题的发布需要使用唤醒词“你好”来触发。※※※

ASR测试效果-02

3.3 LLM(hbm量化格式)

参考来源:DeepSeek大语言模型 | RDK DOC

  1. 下载LLM量化模型(.hbm格式)

在S100(P)系统中创建一个工作目录(如~/Audio_LLM/),下载量化模型到本地,这里以官方支持的DeepSeek_R1_Distill_Qwen_1.5B作为示例。

mkdir Audio_LLM && cd Audio_LLM
wget -c ftp://oeftp@sdk.d-robotics.cc/oe_llm/model/DeepSeek_R1_Distill_Qwen_1.5B_1024.hbm --ftp-password=Oeftp~123$%
  1. 添加配置文件,从tros中复制
# config中为示例使用的模型配置文件
cp -r /opt/tros/humble/lib/hobot_xlm/config/ .

添加后的工作目录结构如下所示:

  1. 默认系统配置即可体验该功能。若模型参数量大,可以尝试修改系统配置来满足推理需求。
# 设置 ION 内存空间最大, 满足大模型推理需求
# S100建议体验1.5B模型,设置ION分配策略为balanced
# S100P则推荐体验7B模型,设置ION分配策略为bpu_first
/usr/hobot/bin/hb_switch_ion.sh balanced # 更激进的话,使用bpu_first参数
reboot
# 设置性能模式(仅S100P)
devmem 0x2b047000 32 0x99
devmem 0x2b047004 32 0x99
  1. 在当前工作目录下(存在模型文件和config配置),启动订阅发布方式节点:
source /opt/tros/humble/setup.bash
lib=/opt/tros/humble/lib/hobot_xlm/lib
export LD_LIBRARY_PATH=${lib}:${LD_LIBRARY_PATH}

# 启动
ros2 run hobot_xlm hobot_xlm --ros-args -p feed_type:=1 \
    -p ros_string_sub_topic_name:="/prompt_text" \
    -p model_name:="DeepSeek_R1_Distill_Qwen_1.5B"
  1. 手动发布/prompt_text话题:
 ros2 topic pub --once /prompt_text std_msgs/msg/String "{data: ""Please make a self-introduction in Chinese""}"

测试示例如下:

4、语音交互

  • 基本的语音交互流程

用户音频输入 —> ASR语音识别,发布提示词话题(如/prompt_text) —> LLM接收提示词话题,推理后发布语音文本话题(如/tts_text) —> TTS接收语音文本话题,输出音频。

4.1 基于hbm量化模型

以下流程主要用于LLM的hbm量化模型实现语音交互
板端audio_llm功能包仓库: Willian-Marshell/audio_llm

  1. 从github仓库中获取audio_llmpython功能包,在工作空间下进行编译,编译后source:source install/setup.bash

  2. 编译完成后,即可在LLM的工作目录(包含模型和配置文件)下运行启动文件了。

cd /path/to/your/hbm_llm #该目录下需要有hbm模型文件、config配置目录
# 启动launch
ros2 launch audio_llm audio_xlm.launch.py

语音交互测试效果如下:

4.2 基于gguf量化模型

除了使用hbm量化形式的LLM来进行语音交互外,还可以使用gguf量化形式的LLM,llama-cpp原生支持gguf格式LLM,因此可以使用tros的hobot-llamacpp算法包来进行推理。

  1. 新建Llama_LLM工作目录、安装算法包及模型
sudo apt update
# 安装llamacpp及其依赖包
sudo apt install tros-humble-hobot-llamacpp tros-humble-hobot-shm

# 新建工作目录
mkdir Llama_LLM && cd Llama_LLM
# LLM模型、ViT模型下载
wget https://hf-mirror.com/D-Robotics/InternVL2_5-1B-GGUF-BPU/resolve/main/Qwen2.5-0.5B-Instruct-Q4_0.gguf
wget https://hf-mirror.com/D-Robotics/InternVL2_5-1B-GGUF-BPU/resolve/main/rdks100/vit_model_int16.hbm
# 从tros工作空间中复制配置文件
cp -r /opt/tros/humble/lib/hobot_llamacpp/config/ .
  1. 模型测试

Llama_LLM工作目录下,使用静态图片对ViT和LLM进行测试:

ros2 run hobot_llamacpp hobot_llamacpp --ros-args -p feed_type:=0 \
    -p image:=config/image2.jpg \
    -p image_type:=0 -p user_prompt:="描述一下这张图片." \
    -p model_file_name:=vit_model_int16.hbm \
    -p llm_model_name:=Qwen2.5-0.5B-Instruct-Q4_0.gguf

  1. 语音交互

通过hobot_llamacpp中的launch文件进行启动,这里将其中的两个launch文件copy到前面创建的功能包audio_llm中,并执行编译。

# 复制启动文件
cd /path/to/rdk_ws/src/audio_llm/launch/
cp /opt/tros/humble/share/hobot_llamacpp/launch/llama_llm.launch.py .
cp /opt/tros/humble/share/hobot_llamacpp/launch/llama_vlm.launch.py .

# 编译
cd /path/to/your/rdk_ws
colcon build

编译完成后在Llama_LLM工作目录(包含模型和配置文件)下运行即可,效果如下。

# 启动llm对话,选择对应的音频设备
ros2 launch audio_llm llama_llm.launch.py audio_device:="plughw:1,0"

# 选择相机类型
export CAM_TYPE=usb #或者mipi
source rdk_ws/install/setup.bash
cd llama_VLM/
# 启动ViT+LLM图文对话,同时指定vit模型名称、选择对应的音频设备
ros2 launch audio_llm llama_vlm.launch.py \
    llamacpp_vit_model_file_name:=vit_model_int16.hbm \
    audio_device:="plughw:1,0"

附录(创建audio_llm功能包)

创建ros2工作空间、新建python功能包

mkdir -p rdk_ws/src
cd rdk_ws/src
ros2 pkg create --build-type ament_python audio_llm

在功能包目录下创建launch文件夹并添加audo_xlm.launch.py启动文件。

cd audio_llm
mkdir launch && cd launch
nano audio_xlm.launch.py
#打开文件后写入如下代码
import os
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
    
    push_wakeup = LaunchConfiguration('push_wakeup', default='0')
    asr_pub_topic_name = LaunchConfiguration('asr_pub_topic_name', default='/prompt_text')
    micphone_name = LaunchConfiguration('micphone_name', default='plughw:1,0')
    playback_device = LaunchConfiguration('playback_device', default='plughw:1,0')
    feed_type = LaunchConfiguration('feed_type', default='1')
    ros_string_sub_topic_name = LaunchConfiguration('ros_string_sub_topic_name', default='/prompt_text')
    model_name = LaunchConfiguration('model_name', default='DeepSeek_R1_Distill_Qwen_1.5B')

    # 为 hobot_xlm 节点设置 LD_LIBRARY_PATH
    # 注意:原始命令中的路径是 /opt/tros/humble/lib/hobot_xlm/lib
    xlm_lib_path = '/opt/tros/humble/lib/hobot_xlm/lib'
    current_ld_path = os.environ.get('LD_LIBRARY_PATH', '')
    if current_ld_path:
        new_ld_path = f"{xlm_lib_path}:{current_ld_path}"
    else:
        new_ld_path = xlm_lib_path
    
    # 添加 ROS_LOG_DIR 环境变量
    ros_log_dir = '/tmp/ros_logs'   # 或者任何现有可写目录,如 '/root/.ros/log'
    os.makedirs(ros_log_dir, exist_ok=True)
    
    hobot_xlm_env = {
        'LD_LIBRARY_PATH': new_ld_path,
        'ROS_LOG_DIR': ros_log_dir
    }

    return LaunchDescription([
        # 声明 launch 参数,允许外部修改
        DeclareLaunchArgument('push_wakeup', default_value='0',
                              description='Whether to push wakeup (0 or 1)'),
        DeclareLaunchArgument('asr_pub_topic_name', default_value='/prompt_text',
                              description='Topic name for ASR output'),
        DeclareLaunchArgument('micphone_name', default_value='plughw:1,0',
                              description='Micphone device name (ALSA)'),
        DeclareLaunchArgument('playback_device', default_value='plughw:1,0',
                              description='Playback device for TTS'),
        DeclareLaunchArgument('feed_type', default_value='1',
                              description='Feed type for hobot_xlm'),
        DeclareLaunchArgument('ros_string_sub_topic_name', default_value='/prompt_text',
                              description='Input topic for hobot_xlm (ASR result)'),
        DeclareLaunchArgument('model_name', default_value='DeepSeek_R1_Distill_Qwen_1.5B',
                              description='Model name for LLM'),

        # 1. sensevoice_ros2 节点
        Node(
            package='sensevoice_ros2',
            executable='sensevoice_ros2',
            name='sensevoice_ros2',
            output='screen',
            parameters=[{
                'push_wakeup': push_wakeup,
                'asr_pub_topic_name': asr_pub_topic_name,
                'micphone_name': micphone_name,
            }]
        ),

        # 2. hobot_tts 节点
        Node(
            package='hobot_tts',
            executable='hobot_tts',
            name='hobot_tts',
            output='screen',
            parameters=[{
                'playback_device': playback_device,
            }]
        ),

        # 3. hobot_xlm 节点(需要额外环境变量)
        Node(
            package='hobot_xlm',
            executable='hobot_xlm',
            name='hobot_xlm',
            output='screen',
            parameters=[{
                'feed_type': feed_type,
                'ros_string_sub_topic_name': ros_string_sub_topic_name,
                'model_name': model_name,
            }],
            env=hobot_xlm_env,
        ),
    ])

同时修改setup.pypackage.xml

import os
from glob import glob
from setuptools import find_packages, setup

package_name = 'audio_llm'

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, 'launch'), glob('launch/*.launch.py')),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='root',
    maintainer_email='root@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    extras_require={
        'test': [
            'pytest',
        ],
    },
    entry_points={
        'console_scripts': [
        ],
    },
)

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>audio_llm</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="root@todo.todo">root</maintainer>
  <license>TODO: License declaration</license>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>
  
  <exec_depend>ros2launch</exec_depend>
  <exec_depend>sensevoice_ros2</exec_depend>
  <exec_depend>hobot_tts</exec_depend>
  <exec_depend>hobot_xlm</exec_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>

全部文件修改后,在工作空间下编译即可。

2 个赞