Skip to main content

如何在 reComputer Jetson 上运行带语音交互的 VLM

简介

本指南详细介绍了如何在 reComputer Nvidia Jetson 设备上运行带语音交互的多模态视觉语言模型(VLM)。该模型结合了 Nvidia Jetson 平台的强大计算能力、阿里巴巴开源的语音转文字模型 SenseVoice 和 coqui-ai 的文字转语音(TTS)模型,用于执行复杂的多模态任务。通过本指南,您将能够成功安装并运行该系统,使其具备视觉识别和语音交互功能,从而为您的项目提供更智能的解决方案。

VLM(视觉语言模型)简介

视觉语言模型(VLM)是一种针对 Nvidia Jetson 平台优化的多模态模型。它结合了视觉和语言处理能力,可处理复杂任务,例如对象识别和生成描述性语言。VLM 可应用于自动驾驶、智能监控和智能家居等领域,提供智能且直观的解决方案。

SenseVoice 简介

SenseVoice 是一个专注于高精度多语言语音识别、语音情感识别和音频事件检测的开源模型。该模型基于超过 40 万小时的数据训练,支持 50 多种语言,并在性能上超越了 Whisper 模型。SenseVoice-Small 模型具有超低延迟,仅需 70 毫秒即可处理 10 秒的音频。它还提供便捷的微调功能,并支持 Python、C++、HTML、Java 和 C# 等多种语言的部署。

TTS(文字转语音)简介

TTS 模型是一种用于文字转语音任务的高性能深度学习模型。它包括 Tacotron2 等多种模型以及 MelGAN 和 WaveRNN 等声码器。TTS 模型支持多说话人 TTS、高效训练,并提供数据集整理和模型测试工具。其模块化代码库便于实现新功能。

前置条件

  • 配备 16GB 或以上内存的 reComputer Jetson AGX Orin 64G 或 reComputer Jetson J4012 16G 设备。
  • USB 即插即用扬声器麦克风。
  • 一台能够输出 RTSP 流地址的 IP 摄像头。我们还提供了使用 NVIDIA Nvstreamer 工具将本地视频转换为 RTSP 流的说明。
note

我们已经在 reComputer Orin NX 16GBAGX Orin 64GB 开发套件上测试了本教程的可行性。

安装

初始化系统环境

  1. 在使用 JP6 安装初始系统后,需要检查 CUDA 和其他库的安装情况。可以通过运行以下命令验证并安装它们:

    sudo apt-get install nvidia-jetpack
  2. 安装 python3-pipjtopdocker-ce

  3. 通过以下命令安装必要的依赖项:

    sudo apt-get install libportaudio2 libportaudiocpp0 portaudio19-dev
    sudo pip3 install pyaudio playsound subprocess wave keyboard
    sudo pip3 --upgrade setuptools
    sudo pip3 install sudachipy==0.5.2
  4. 检查音频输入输出以及 USB 扬声器麦克风是否正常工作,并确保网络连接稳定。

安装 VLM

本项目的核心功能是视觉语言模型(VLM)。我们提供了如何在 reComputer Nvidia Jetson 上运行 VLM 的指南。请参考此链接获取安装和使用说明。在继续以下步骤之前,请确保您完全理解如何使用 VLM 进行文本描述推理。

安装 Pytorch 和 Torchaudio

我们为初学者提供了 Nvidia Jetson AI 课程,其中包含如何安装 PyTorch、Torchaudio 和 Torchvision 的说明。请根据您的系统环境下载并安装这些包。

安装 Speech_vlm(基于 SenseVoice)

  1. 克隆 Speech_vlm 包:
    cd ~/
    git clone https://github.com/ZhuYaoHui1998/speech_vlm.git
  2. 安装 Speech_vlm 环境:
    cd ~/speech_vlm
    sudo pip3 install -r requement.txt

安装 TTS(基于 Coqui-ai)

cd ~/speech_vlm/TTS
sudo pip3 install .[all]

使用方法

Speech_vlm 仓库的结构如下:

speech_vlm/
├── /TTS # Coqui-ai TTS 程序
├── config # VLM 配置
├── README.md # 项目介绍
├── requirements.txt # SenseVoice 所需环境库
├── compose.yaml # VLM Docker Compose 启动文件
├── delete_id.sh # 删除摄像头 ID 脚本
├── example_1.wav # 音频反馈音调模板(可替换)
├── model.py # SenseVoice 主程序
├── set_alerts.sh # 设置摄像头警报
├── set_describe.sh # 文本输入以让 VLM 描述当前场景
├── set_streamer_id.sh # 添加 RTSP 摄像头到 VLM
├── view_rtsp.py # 使用 opencv 查看 RTSP 流
└── vlm_voice.py # 多模态主程序
  1. 启动 VLM

    cd ~/speech_vlm
    sudo docker compose up -d
  2. 添加 RTSP 流到 VLM

查看 speech_vlm 仓库下的 set_streamer_id.sh 内容:

#!/bin/bash
curl --location 'http://0.0.0.0:5010/api/v1/live-stream' \
--header 'Content-Type: application/json' \
--data '{"liveStreamUrl": "RTSP stream address"}'

0.0.0.0 替换为 Jetson 设备的 IP 地址,并将 RTSP stream address 替换为摄像头的 RTSP 流地址。 例如:

#!/bin/bash
curl --location 'http://192.168.49.227:5010/api/v1/live-stream' \
--header 'Content-Type: application/json' \
--data '{"liveStreamUrl": "rtsp://admin:[email protected]:554//Streaming/Channels/1"}'
note

如果您没有 RTSP 摄像头,我们提供了如何使用 NVStreamer 将本地视频流式传输为 RTSP以及将其添加到 VLM的说明。

运行 set_streamer_id.sh

cd ~/speech_vlm
sudo chmod +x ./set_streamer_id.sh
./set_streamer_id.sh

我们将获得一个摄像头 ID,此 ID 非常重要,需要记录下来,如下所示:

  1. 运行 vlm_voice.py

您需要替换以下 Python 代码中的 0.0.0.0

API_URL = 'http://0.0.0.0:5010/api/v1/chat/completions'  # API 端点
REQUEST_ID = "" # 请求 ID

将其替换为 Jetson 的 IP 地址,并将步骤 2 返回的摄像头 ID 填入 REQUEST_ID

vlm_voice.py
import pyaudio
import wave
import keyboard
import subprocess
import json
from funasr import AutoModel
from funasr.utils.postprocess_utils import rich_transcription_postprocess
import time
import torch
from TTS.api import TTS
import os
# 获取设备
device = "cuda" if torch.cuda.is_available() else "cpu"

# 初始化 TTS
api = TTS("tts_models/en/ljspeech/glow-tts").to(device)

# 配置参数
FORMAT = pyaudio.paInt16 # 16 位分辨率
CHANNELS = 1 # 单声道
CHUNK = 1024 # 每块样本数
OUTPUT_FILENAME = "output.wav" # 输出文件名
API_URL = 'http://192.168.49.227:5010/api/v1/chat/completions' # API 端点
REQUEST_ID = "1388b691-3b9f-4bda-9d70-0ff0696f80f4" # 请求 ID

# 初始化 PyAudio
audio = pyaudio.PyAudio()
# 准备存储录音数据的列表
frames = []

# 初始化麦克风采样率
print("可用的音频输入设备:")
for i in range(audio.get_device_count()):
info = audio.get_device_info_by_index(i)
print(f"设备 {i}: {info['name']} - {info['maxInputChannels']} 通道")

device_index = int(input("请选择 USB 麦克风的设备索引:"))

device_info = audio.get_device_info_by_index(device_index)
supported_sample_rates = [8000, 16000, 32000, 44100, 48000]
supported_rate=0
for rate in supported_sample_rates:
try:
if audio.is_format_supported(rate,
input_device=device_index,
input_channels=1,
input_format=pyaudio.paInt16):
supported_rate=rate
print(f"{rate} Hz 是支持的。")
except ValueError:
print(f"{rate} Hz 不支持。")


# 初始化模型
model = "./SenseVoiceSmall"
model = AutoModel(
model=model,
vad_model="./speech_fsmn_vad_zh-cn-16k-common-pytorch",
vad_kwargs={"max_single_segment_time": 30000},
trust_remote_code=True,
disable_log=True
)



def extract_content(json_response):
try:
# 解析 JSON 字符串
data = json.loads(json_response)

# 提取 content 部分
content = data["choices"][0]["message"]["content"]

print(f"{content}")
return content
except KeyError as e:
print(f"键错误: {e}")
except json.JSONDecodeError as e:
print(f"JSON 解码错误: {e}")
except Exception as e:
print(f"发生了意外错误: {e}")

def start_recording():
global frames
frames = []

try:
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=supported_rate, input=True,
frames_per_buffer=CHUNK, input_device_index=device_index)
print("录音开始... 按 '2' 停止录音。")

while True:
if keyboard.is_pressed('2'):
print("录音停止。")
break
data = stream.read(CHUNK)
frames.append(data)

stream.stop_stream()
stream.close()

except Exception as e:
print(f"录音过程中发生错误: {e}")

def save_recording():
try:
waveFile = wave.open(OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(supported_rate)
waveFile.writeframes(b''.join(frames))
waveFile.close()
print(f"录音已保存为 {OUTPUT_FILENAME}")
except Exception as e:
print(f"保存录音时发生错误: {e}")

def send_alert(text):
# 构造 JSON 负载
payload = {
"messages": [
{
"role": "system",
"content": "You are a helpful AI assistant."
},
{
"role": "user",
"content": [
{
"type": "stream",
"stream": {
"stream_id": REQUEST_ID
}
},
{
"type": "text",
"text": text
}
]
}
],
"min_tokens": 1,
"max_tokens": 128
}

# 将负载转换为 JSON 字符串
json_payload = json.dumps(payload)

# 使用 subprocess 执行 curl 命令
curl_command = [
'curl', '--location', API_URL,
'--header', 'Content-Type: application/json',
'--data', json_payload
]

try:
result = subprocess.run(curl_command, check=True, capture_output=True, text=True)
## 获取内容
content_result=extract_content(result.stdout)
# TTS
api.tts_to_file(
str(content_result),
speaker_wav="./example_1.wav",
file_path="speech.wav"
)
# 转换音频采样率
subprocess.run(['ffmpeg', '-i', 'speech.wav', '-ar',str(supported_rate), 'speech1.wav','-y'])
# 播放音频
wf = wave.open('./speech1.wav', 'rb')
stream = audio.open(format=pyaudio.paInt16,
channels=1,
rate=supported_rate,
output=True,
output_device_index=device_index)
data = wf.readframes(1024)
while data:
stream.write(data)
data = wf.readframes(1024)
# 播放音频
os.remove('speech.wav')
os.remove('speech1.wav')
stream.stop_stream()
stream.close()
wf.close() # 同时关闭 wave 文件

#print(f"警报发送成功: {result.stdout}")
except subprocess.CalledProcessError as e:
print(f"发送警报时发生错误: {e.stderr}")
finally:
# 即使发生错误,也尝试关闭流
if stream.is_active():
stream.stop_stream()
os.remove('speech.wav')
os.remove('speech1.wav')
stream.close()
print("欢迎使用录音和语音转文本系统!")
print("按 '1' 开始录音,按 '2' 停止录音。")

while True:
if keyboard.is_pressed('1'):
print("准备开始录音...")
start_recording()
save_recording()

print("正在处理录音文件,请稍候...")
try:
res = model.generate(
input=f"./{OUTPUT_FILENAME}",
cache={},
language="auto", # "zh", "en", "yue", "ja", "ko", "nospeech"
use_itn=True,
batch_size_s=60,
merge_vad=True,
merge_length_s=15,
)
text = rich_transcription_postprocess(res[0]["text"])
print(f"语音转文本结果:\n{text}")

# 将转录结果作为警报发送
send_alert(text)

except Exception as e:
print(f"处理录音时发生错误: {e}")

time.sleep(0.1) # 减少 CPU 使用率

运行 Python:

cd ~/speech_vlm
sudo python3 vlm_voice.py

程序启动后,它将扫描所有音频输入和输出设备。您需要手动选择所需音频设备的索引 ID。程序即将开始工作,然后按 1 进行录音,按 2 发送。

  1. 查看结果

我们准备了一个 view_rtsp.py 脚本来查看输出结果。您需要将 rtsp_url = "rtsp://0.0.0.0:5011/out" 中的 IP 部分替换为您的 Jetson 设备的 IP 地址。

viwe_rtsp.py
import cv2

rtsp_url = "rtsp://192.168.49.227:5011/out"

cap = cv2.VideoCapture(rtsp_url)

if not cap.isOpened():
print("无法打开 RTSP 流")
exit()

while True:
ret, frame = cap.read()
if not ret:
print("无法检索帧")
break

height, width = frame.shape[:2]

frame_resized = cv2.resize(frame, (int(width // 1.1), int(height // 1.1)))

cv2.imshow('RTSP 流', frame_resized)

if cv2.waitKey(1) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()
sudo pip3 install opencv-python
cd ~/speech_vlm
sudo python3 view_rtsp.py

演示

技术支持与产品讨论

感谢您选择我们的产品!我们将为您提供多种支持,以确保您使用我们的产品时体验顺畅。我们提供多个沟通渠道,以满足不同的偏好和需求。

Loading Comments...