Jetson Orin Nano Super 8GB 离线大模型全栈实战:Ollama + Open WebUI + VLM 视觉检测 + reSpeaker 远场语音交互 + 离线语音助手
本文是 Seeed Jetson 暑期训练营 Week3 的完整实践笔记,主题为「离线大语言模型应用开发」。
在 Jetson Orin Nano Super 8GB 上,从零完成了四大模块:本地大模型部署(Ollama + Open WebUI)、视觉语言模型(VLM)图像理解、reSpeaker Flex 麦克风阵列的远场语音交互(多通道拾音 / 声源定位 / 降噪),以及一套完全离线的语音助手(本地 ASR + LLM + TTS 完整链路)。
所有步骤均实机验证通过,包含完整命令、代码、运行结果,以及一路踩坑的排查记录,可作为 Jetson 平台离线 AI 应用开发的入门参考。
Jetson Orin Nano Super 8GB 离线大模型全栈实战:Ollama + Open WebUI + VLM 视觉检测 + reSpeaker 远场语音交互 + 离线语音助手(ASR+LLM+TTS)全记录
一、引言
二、硬件与系统环境
2.1 硬件清单2.2 软件版本
三、本地大模型部署(Ollama + Open WebUI)
3.1 安装 Ollama3.2 安装 Open WebUI(Docker 方式)3.3 访问 Web 界面3.4 模型选型
四、视觉语言模型(VLM)图像理解4.1 克隆 Seeed 教程仓库4.2 安装 Python 客户端4.3 下载视觉语言模型4.4 视觉检测脚本调试与修复4.5 局限性分析
五、远场语音交互(reSpeaker Flex 麦克风阵列)
5.1 麦克风阵列识别与验证5.2 多通道录音与远场拾音5.3 声源定位原理与实现(DOA)5.3.1 能量法原理5.3.2 Python 实现5.3.3 算法局限性5.4 降噪体验
六、离线语音助手(ASR + LLM + TTS)
6.1 整体架构6.2 组件安装6.2.1 Vosk(离线语音识别)6.2.2 Piper TTS(离线语音合成)6.2.3 Ollama 与模型6.3 语音助手脚本实现6.4 运行与测试
七、踩坑记录与解决方案
八、优化与扩展建议
九、总结与展望致谢参考文献
一、引言
随着边缘计算与端侧 AI 的快速发展,在低功耗嵌入式设备上运行大语言模型(LLM)和视觉语言模型(VLM)正成为刚需。Jetson 系列凭借强大的 GPU 算力与统一内存架构,是边缘 AI 部署的理想平台。但受限于 8GB 的共享内存与 ARM64 架构,如何选型、部署、调用本地大模型,并将其延伸到语音交互,是本周的核心命题。
本文选择 NVIDIA Jetson Orin Nano Super 8GB 作为主控,完成以下四个实践:
-
使用 Ollama + Open WebUI 部署本地大模型,实现浏览器端对话;
-
使用 moondream 视觉语言模型,实现图像理解与目标检测;
-
使用 Seeed reSpeaker Flex 环形麦克风阵列,体验远场拾音、声源定位(DOA)与降噪;
-
整合 Vosk + Ollama + Piper,搭建完全离线的语音助手(ASR → LLM → TTS)。
整个系统具备「完全离线运行、轻量模型适配、隐私本地化」三个特点。
二、硬件与系统环境
2.1 硬件清单
| 设备 | 型号 / 规格 | 备注 |
|---|---|---|
| 开发板 | NVIDIA Jetson Orin Nano Super 8GB | JetPack 6,Ubuntu 22.04 |
| 麦克风阵列 | Seeed reSpeaker Flex Circular | USB 接口,4 个环形麦克风,内置 XMOS XVF3800 音频处理器 |
| 摄像头 | IMX219 广角 MIPI | 120° 视场角,CSI 排线直连(沿用 Week2) |
| 存储 | 64GB+ microSD / NVMe | 系统与模型存储 |
| 音频输出 | 有线耳机 / 蓝牙耳机 | 用于播放 TTS 结果 |
| 网络 | 有线 / WiFi | 仅用于下载依赖与模型,运行时可完全离线 |
2.2 软件版本
| 组件 | 版本 | 说明 |
|---|---|---|
| Python | 3.10 | JetPack 6 自带 |
| Ollama | 0.32.x | 本地大模型运行框架 |
| Open WebUI | ghcr.io/open-webui/open-webui:main | 本地大模型 Web 界面(Docker 部署) |
| Vosk | 0.3.45 | 离线语音识别(ASR) |
| Piper TTS | 1.7.0 | 离线语音合成(TTS) |
| Docker | 29.x | 用于部署 Open WebUI |
| ALSA | — | reSpeaker Flex 免驱,USB 音频类设备 |
⚠️ 关于内存:Jetson 采用「统一内存架构」,CPU 与 GPU 共享 8GB LPDDR5。运行大模型时必须时刻关注可用内存,这是选型的硬约束,后文会多次提到。
三、本地大模型部署(Ollama + Open WebUI)
3.1 安装 Ollama
Ollama 是一个轻量级的本地大模型运行框架,官方提供了适配 JetPack / ARM64 的安装脚本,一行命令即可完成安装:
curl -fsSL https://ollama.com/install.sh | sh
安装过程会自动完成以下工作:
-
下载并解压
ollama-linux-arm64.tar.zst; -
创建
ollama用户及用户组; -
将当前用户加入
ollama组; -
注册
systemd服务并启动。
安装完成后验证:
ollama --version # 查看版本(本文使用 0.32.x) systemctl status ollama # 服务状态应为 active (running)
踩坑:权限问题。若执行 ollama 命令出现权限错误,执行以下命令将当前用户加入 ollama 组,并重新登录或使用 newgrp 刷新组权限:
sudo usermod -aG ollama $USER newgrp ollama
3.2 安装 Open WebUI(Docker 方式)
Open WebUI 是一个功能丰富的 Web 界面,可通过 Docker 快速部署。在 Jetson 上推荐使用 --network=host 模式,让容器直接访问本机的 Ollama 服务(127.0.0.1:11434)。
第一步:解决 Docker 权限
sudo usermod -aG docker $USER newgrp docker
验证(不再报 permission denied 即可):
docker ps
第二步:拉取镜像并运行容器
docker pull ghcr.io/open-webui/open-webui:main docker run -d \ --name open-webui \ --restart always \ --network=host \ -v open-webui:/app/backend/data \ ghcr.io/open-webui/open-webui:main
关键参数说明:
| 参数 | 作用 |
|---|---|
--network=host |
使用宿主机网络,容器可直接访问 127.0.0.1:11434 的 Ollama 服务 |
-v open-webui:/app/backend/data |
数据卷持久化用户数据、聊天记录等 |
--restart always |
开机自启 / 崩溃自动重启 |
3.3 访问 Web 界面
浏览器打开 http://<Jetson-IP>:8080,首次访问需注册一个本地管理员账号。登录后,Open WebUI 会自动识别本地 Ollama 中已下载的模型。
📷 配图 1:
docker run部署 Open WebUI +ollama list列出本地模型(qwen2.5:0.5b)的终端截图📷 配图 2:Open WebUI 对话界面(浏览器访问
http://192.168.1.106:8080)
3.4 模型选型
由于 8GB 统一内存的限制,模型选型非常关键。本次使用的核心对话模型为:
ollama pull qwen2.5:0.5b
-
qwen2.5:0.5b:约 397MB,运行时内存占用约 500MB,在 8GB 设备上非常流畅,作为离线语音助手的「大脑」再合适不过。
关于更大模型的说明:后续也尝试过 llava:7b、deepseek-r1:7b 等,但 7B 级模型至少需要 5GB 以上可用内存,而设备扣除系统占用后可用内存往往不足 3GB,强行加载推理速度极低、不具备实用性。结论:8GB 设备请优先选择 0.5B~3B 级别的量化模型。
四、视觉语言模型(VLM)图像理解
4.1 克隆 Seeed 教程仓库
为了学习 Jetson 上的大模型应用,克隆 Seeed 官方入门仓库:
git clone https://github.com/Seeed-Projects/reComputer-Jetson-for-Beginners
该仓库包含多个章节,其中第五章涉及离线大模型开发,包含视觉检测示例代码。
踩坑:Git 网络问题。克隆过程中遇到 Git 认证或 TLS 握手失败,主要表现为:
gnutls_handshake() failed
原因是使用虚拟网卡模式(TUN)时,Git 的 TLS 握手异常。解决方案:切换为「系统代理」模式,或手动为 Git 设置 HTTP 代理:
git config --global http.proxy http://127.0.0.1:端口 git config --global https.proxy http://127.0.0.1:端口
4.2 安装 Python 客户端
pip install ollama
4.3 下载视觉语言模型
选择轻量级视觉语言模型 moondream:
ollama pull moondream
-
moondream:约 1.7GB,在 Jetson 上运行流畅,擅长图片描述与简单物体识别。
4.4 视觉检测脚本调试与修复
仓库中的 vlm_object_detector.py 在运行时出现了两个典型问题,逐一修复如下。
问题一:模型输出 JSON 不完整(截断)
最初运行脚本时,模型生成的 JSON 因未限制 max_tokens 而被截断,导致解析失败。修复方法是在 ollama.generate 中显式指定参数:
response = ollama.generate(
model=args.model,
prompt=prompt,
images=[img_b64],
format='json', # 强制输出合法 JSON
options={'max_tokens': 1024, 'temperature': 0}, # 防止截断,降低随机性
stream=False
)
问题二:解析逻辑不完善
脚本原解析代码假设模型返回 JSON 数组,但实际可能返回单个 JSON 对象,导致 'str' object has no attribute 'get' 错误。修复后兼容两种格式:
data = json.loads(raw)
if isinstance(data, list):
items = data
elif isinstance(data, dict):
items = [data]
else:
items = []
for item in items:
if isinstance(item, dict):
label = item.get('label', 'unknown')
bbox = item.get('bbox_2d', [])
print(f" - {label} 位置(归一化): {bbox}")
运行修复后的脚本(注意先清除代理环境变量,见第七章):
env -u ALL_PROXY -u all_proxy -u http_proxy -u https_proxy python vlm_object_detector.py \ --image ../images/5-6-asr-llm-tts-02.png --model moondream
输出示例:
正在调用模型,请稍候...
模型原始输出:
{"label": "person", "bbox_2d": [0.1, 0.2, 0.3, 0.4]}
检测结果:
- person 位置(归一化): [0.1, 0.2, 0.3, 0.4]
📷 配图 3:
vlm_object_detector.py运行输出 + Seeed「AI 语音处理流水线」示意图
4.5 局限性分析
为直观展示检测结果,使用 Pillow 在图片上绘制检测框并保存为 output_detection.jpg。但实测发现 moondream 给出的坐标偏差较大,框选位置与真实物体不符。原因分析:
-
moondream 属于「视觉语言模型」,擅长图像内容描述,而非精确目标定位;
-
模型推理时会缩放图像,导致归一化坐标精度下降;
-
对复杂背景或小物体,定位能力尤为有限。
结论:如果需要精确的目标检测框,应使用专门的目标检测模型(如 YOLO / TensorRT 加速),而非依赖 VLM 输出的坐标。VLM 更适合「看图说话」式的语义理解。
五、远场语音交互(reSpeaker Flex 麦克风阵列)
5.1 麦克风阵列识别与验证
reSpeaker Flex 通过 USB 连接后,Jetson 会自动识别为 USB 声卡,无需额外驱动。使用 arecord -l 查看设备:
card 2: C16K6Ch [reSpeaker Flex XVF3800 C16K6Ch], device 0: USB Audio [USB Audio]
确认物理通道数(实际为 4 个麦克风):
arecord -D plughw:2,0 --dump-hw-params
⚠️ 输出中的
CHANNELS范围很大(如[1 10000]),并不代表物理通道数,需通过实际录音测试确认。测试 4 通道录音:
arecord -D plughw:2,0 -c 4 -d 2 -f S16_LE -r 16000 test_4ch.wav
若成功,则确认 4 通道可用。也可测试 6 通道,但 4 通道更符合环形阵列设计。
5.2 多通道录音与远场拾音
录制 4 通道远场音频(距离 2~3 米):
arecord -D plughw:2,0 -c 4 -d 5 -f S16_LE -r 16000 farfield_4ch.wav
单通道近 / 远场对比录音:
arecord -D plughw:2,0 -c 1 -d 5 -f S16_LE -r 16000 near_single.wav arecord -D plughw:2,0 -c 1 -d 5 -f S16_LE -r 16000 far_single.wav
通过播放或查看波形,可以直观体验硬件降噪和远场拾音效果。
5.3 声源定位原理与实现(DOA)
5.3.1 能量法原理
环形麦克风阵列中,声源到达不同麦克风的距离不同,导致各通道接收能量存在差异。当声源正对某个麦克风时,该通道能量最大。对于 4 个均匀分布的麦克风(间隔 90°),通过比较各通道的 RMS 能量,即可粗略估计声源方向。
5.3.2 Python 实现
创建 doa_energy.py:
import wave
import numpy as np
wav_file = "farfield_4ch.wav"
wf = wave.open(wav_file, "rb")
channels = wf.getnchannels()
rate = wf.getframerate()
frames = wf.readframes(wf.getnframes())
wf.close()
data = np.frombuffer(frames, dtype=np.int16).reshape(-1, channels)
energy = np.sqrt(np.mean(data.astype(np.float32)**2, axis=0))
print("各通道能量:")
for i, e in enumerate(energy):
print(f" Ch{i}: {e:.2f}")
max_ch = int(np.argmax(energy))
angle = max_ch * (360 / channels) # 4通道 -> 每通道90°
print(f"\n估计声源方向:约 {angle} 度(通道 {max_ch})")
运行结果示例:
各通道能量: Ch0: 2749.99 Ch1: 203.42 Ch2: 97.80 Ch3: 103.24 估计声源方向:约 0.0 度(通道 0)
可以看到 Ch0 能量远超其他通道,声源方向估计为 0°,与实际情况吻合。
📷 配图 4:
doa_energy.py运行输出(各通道能量 + 声源方向估计)
5.3.3 算法局限性
能量法简单易实现,但精度有限(约 ±45°),无法应对多声源、混响等复杂场景。实际产品中常采用 GCC-PHAT、MUSIC、SRP-PHAT 等高分辨率算法。本文旨在入门体验,后续可扩展。
5.4 降噪体验
reSpeaker Flex 内置 XMOS XVF3800 音频处理器,支持波束成形、回声消除和噪声抑制,通过 USB 输出时已进行初步处理。可通过以下方式体验:
-
对比近场 / 远场录音清晰度;
-
在嘈杂环境中录音,感受降噪效果;
-
分析多通道音频,尝试软件波束成形(如 Delay-and-Sum)。
六、离线语音助手(ASR + LLM + TTS)
6.1 整体架构
[麦克风] → [arecord 录音] → [Vosk ASR] → [Ollama LLM] → [Piper TTS] → [WAV 文件/播放]
选型理由:
| 组件 | 选择 | 理由 |
|---|---|---|
| ASR | Vosk | 轻量、离线、支持中文,模型小(约 42MB),适合嵌入式 |
| LLM | Ollama(qwen2.5:0.5b) | 统一本地大模型管理,支持 GPU 加速,内存占用约 500MB |
| TTS | Piper | 基于 ONNX Runtime 的轻量 TTS,中文音质较好 |
6.2 组件安装
6.2.1 Vosk(离线语音识别)
pip install vosk mkdir -p ~/vosk-model && cd ~/vosk-model wget https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip unzip vosk-model-small-cn-0.22.zip mv vosk-model-small-cn-0.22 model-cn
6.2.2 Piper TTS(离线语音合成)
pip install piper-tts cd ~ wget https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx wget https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx.json
6.2.3 Ollama 与模型
curl -fsSL https://ollama.com/install.sh | sh ollama pull qwen2.5:0.5b
6.3 语音助手脚本实现
完整脚本 voice_assistant.py:
import os
import sys
import wave
import json
import subprocess
import vosk
import ollama
# ---------- 参数 ----------
RATE = 16000
RECORD_SECONDS = 5
RECORD_FILE = "/tmp/input.wav"
# ---------- 初始化 Vosk ----------
model_path = os.path.expanduser("~/vosk-model/model-cn")
if not os.path.exists(model_path):
sys.exit("Vosk 模型不存在")
vosk_model = vosk.Model(model_path)
# ---------- Piper 模型路径 ----------
piper_model_path = os.path.expanduser("~/zh_CN-huayan-medium.onnx")
if not os.path.exists(piper_model_path):
sys.exit("Piper 模型不存在")
def record_and_recognize():
print("请说话...")
subprocess.run(["arecord", "-D", "plughw:2,0", "-c", "1", "-d", str(RECORD_SECONDS),
"-f", "S16_LE", "-r", str(RATE), RECORD_FILE],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
wf = wave.open(RECORD_FILE, "rb")
audio_data = wf.readframes(wf.getnframes())
wf.close()
recognizer = vosk.KaldiRecognizer(vosk_model, RATE)
recognizer.AcceptWaveform(audio_data)
result = json.loads(recognizer.FinalResult())
return result.get("text", "").strip()
def speak(text):
wav_file = "/tmp/tts_output.wav"
subprocess.run(["piper", "-m", piper_model_path, "-f", wav_file],
input=text.encode('utf-8'), check=True)
print(f"语音已保存到 {wav_file}")
print("离线语音助手已启动,按 Ctrl+C 退出")
while True:
try:
user_input = record_and_recognize()
if not user_input:
print("未识别到语音,重试...")
continue
print(f"用户说: {user_input}")
response = ollama.chat(model="qwen2.5:0.5b",
messages=[{"role": "user", "content": user_input}])
answer = response['message']['content']
print(f"助手回答: {answer}")
speak(answer)
except KeyboardInterrupt:
print("退出")
break
except Exception as e:
print(f"错误: {e}")
📷 配图 5:
voice_assistant.py运行日志(Vosk 模型加载 + 多轮对话)
6.4 运行与测试
env -u ALL_PROXY -u all_proxy -u http_proxy -u https_proxy python ~/voice_assistant.py
示例输出:
请说话... 用户说: 你好 助手回答: 你好!很高兴能帮助你。... 语音已保存到 /tmp/tts_output.wav
生成的 WAV 文件可通过 HTTP 服务器下载到手机播放:
cd /tmp && python3 -m http.server 8000 # 手机浏览器访问 http://<IP>:8000/tts_output.wav
至此,一套完全离线、纯本地的中文语音助手就跑通了。
七、踩坑记录与解决方案
这是本次实践中最有价值的部分,几乎每一步都踩了坑。整理成表,供同样踩坑的同学速查。
| 问题 | 原因 | 解决方案 |
|---|---|---|
Python 报 Unknown scheme for proxy URL socks:// |
系统代理使用 socks 协议,httpx 库不支持 |
运行前清除代理环境变量:env -u ALL_PROXY -u all_proxy -u http_proxy -u https_proxy python script.py |
pyaudio 安装失败 |
缺少 portaudio.h,apt 源无法连接 |
放弃 pyaudio,改用 arecord 命令录音,Python 读取 WAV 文件 |
Piper Python API 报 'str' object has no attribute 'setframerate' |
Piper API 版本不兼容或参数错误 | 改用 Piper 命令行工具,通过 subprocess.run 从标准输入传文本 |
Vosk 退出时报 vosk_model_free 异常 |
Vosk 对象析构问题 | 忽略该异常,不影响功能 |
| 录音无声音或识别率低 | 音量太低或距离过远 | 调整 alsamixer 音量,靠近麦克风;可换更大 Vosk 模型 |
| 生成 WAV 文件为 0 字节 | Piper 命令执行失败但未捕获 | 手动执行 echo test | piper -m ... -f out.wav 排查 |
| 蓝牙耳机不出声 | 未设置为默认 sink 或未连接 A2DP | pactl set-default-sink bluez_sink...,或直接用有线耳机 |
docker: permission denied |
当前用户不在 docker 组 | sudo usermod -aG docker $USER 后重新登录 |
Git 报 gnutls_handshake() failed |
虚拟网卡代理导致 TLS 异常 | 切换为系统代理,或为 Git 单独设置 HTTP 代理 |
| 模型输出 JSON 截断 | 未设置 max_tokens |
options={'max_tokens': 1024} |
JSON 解析报 'str' object has no attribute 'get' |
模型返回对象而非数组 | 解析时兼容 dict 和 list 两种格式 |
| 内存不足,无法运行大模型 | 8GB 统一内存限制 | 选择轻量模型(如 moondream),停止不必要服务 |
💡 经验总结:报错别慌,顺着日志倒推就能定位。这些坑绝大多数是「环境问题」(代理、权限、依赖),而非代码逻辑问题。
八、优化与扩展建议
-
实时流式识别:使用 Vosk 的流式 API,避免固定长度录音,提升交互自然度;
-
唤醒词检测:集成 Porcupine 或 Snowboy,实现免按键唤醒;
-
更精确的 DOA:采用 GCC-PHAT 或 MUSIC 算法,或利用 reSpeaker Flex 的原始数据进行高级处理;
-
模型优化:
-
ASR:使用更大中文模型(如
vosk-model-cn-0.22); -
LLM:若内存允许,可尝试
qwen2.5:1.5b; -
TTS:调整语速、音量参数,或使用不同音色模型;
-
-
端到端集成:将语音助手与 Open WebUI 结合,实现 Web 端语音交互;
-
多模态扩展:加入摄像头视觉识别,实现「看、听、说」全能助手。
九、总结与展望
本文在 Jetson Orin Nano Super 8GB 上完成了 Week3 的全部实践目标:
-
✅ 使用 Ollama + Open WebUI 部署并访问本地大模型;
-
✅ 使用 moondream 完成 VLM 图像理解与视觉检测;
-
✅ 通过 reSpeaker Flex 完成远场拾音、能量法声源定位与降噪体验;
-
✅ 整合 Vosk + Ollama + Piper,搭建了 ASR → LLM → TTS 的离线语音助手。
整个系统完全运行在边缘设备上,体现了低功耗、高隐私、低延迟的优势,可广泛应用于智能家居、教育机器人、工业巡检等场景。
从 Week1 的环境搭建、Week2 的 OpenCV + YOLO 视觉,到 Week3 的离线大模型与语音交互,Jetson 正在一步步「活」起来。后续计划继续向更精确的目标检测(TensorRT 加速的 YOLO)、实时流式语音交互、多模态融合方向探索。
致谢
感谢 Seeed Studio 和 NVIDIA 提供优秀的硬件平台与文档支持。
参考文献
-
Ollama 官方文档:Ollama
-
Vosk 语音识别:VOSK Offline Speech Recognition API
-
Piper TTS:https://github.com/rhasspy/piper
-
Seeed reSpeaker Flex Wiki:https://wiki.seeedstudio.com/reSpeaker_Flex/
-
Seeed 教程仓库:https://github.com/Seeed-Projects/reComputer-Jetson-for-Beginners
希望本文能为 Jetson 语音与大模型应用开发者提供有价值的参考。如有问题,欢迎在评论区交流讨论。
更多推荐


所有评论(0)