基于Jetson Orin Nano与Ollama的机器人本地语音LLM部署实战

📅 2026/8/2 13:30:09 👁️ 阅读次数 📝 编程学习
基于Jetson Orin Nano与Ollama的机器人本地语音LLM部署实战

1. 项目缘起:为什么要在机器人上跑本地语音LLM?

最近在折腾我的Reachy Mini机器人,一个很酷的开源双臂机器人平台。它本身已经具备不错的视觉和运动能力,但交互方式主要还是靠预设的程序或者远程API调用。我一直想给它加上一个更“自然”的交互入口——语音。想象一下,你直接对它说“把那个红色的方块拿给我”,它就能理解并执行,这体验感一下子就上来了。

市面上现成的语音助手方案很多,比如接个智能音箱的API,或者用云端的语音转文本(STT)和大语言模型(LLM)服务。但把这些方案用在机器人上,有几个硬伤:首先是延迟,云端API的网络往返时间在机器人实时控制场景下是不可接受的,一个指令等个一两秒,体验就全毁了。其次是隐私和成本,机器人摄像头和麦克风采集的音频、视频数据全部上传到第三方,对于很多实验室、创客空间或者有数据安全顾虑的场景来说,是个大问题。最后是可控性,云端模型是个黑盒,你很难针对机器人特定的指令集(比如“向左转30度”、“夹爪力度设为0.5牛”)去做定制化的优化和调试。

所以,本地部署就成了一个必然的选择。把语音识别和语言模型都跑在机器人“体内”的计算单元上,实现端到端的低延迟、高隐私的智能交互。我手头的Reachy Mini配套的计算单元是reComputer Mini,这是一款基于NVIDIA Jetson Orin Nano模组的小型工控机,性能对于边缘AI应用来说相当不错。这个项目的核心目标,就是把一个轻量级的语音LLM pipeline,从云端“搬”到这台reComputer Mini上,让Reachy Mini真正能“听懂人话”,并基于理解做出响应。

2. 硬件与软件栈选型:为什么是它们?

在开始动手之前,得先把“用什么”和“为什么用”这两个问题搞清楚。硬件是给定的(reComputer Mini),但软件栈的每一个组件都需要仔细考量。

2.1 核心硬件:reComputer Mini (Jetson Orin Nano) 的能力边界

reComputer Mini的核心是NVIDIA Jetson Orin Nano模组。我用的这款是8GB内存的版本。对于部署LLM来说,内存是首要的硬约束。一个参数量为7B(70亿)的模型,以FP16精度加载,大约需要14GB的显存,这显然超出了Orin Nano的能力。因此,我们的模型选型必须瞄准更小的参数规模,比如1B到3B左右的模型,或者使用量化技术(如INT4, INT8)来大幅降低显存占用。

Orin Nano的CPU是ARM架构的,GPU则基于NVIDIA Ampere架构,拥有少量但效能不错的CUDA核心,支持TensorRT等加速库。这意味着我们虽然不能跑动辄百亿参数的大模型,但针对小模型进行合理的优化后,获得可用的推理速度(比如每秒生成10-20个token)是完全可行的。另一个优势是功耗和体积,它非常适合作为机器人的“大脑”集成。

2.2 模型服务框架:为什么选择Ollama?

Ollama近半年在开发者社区里火得不行,它本质上是一个简化本地大模型部署和运行的工具。相比于直接去Hugging Face下载模型文件然后用Transformers库加载,Ollama提供了几个关键优势:

  1. 开箱即用:一条命令就能拉取、运行一个模型,自动处理模型格式(GGUF)、上下文长度等配置。
  2. 统一的API:通过简单的REST API(默认端口11434)提供模型交互,这极大简化了应用程序(比如我们的机器人控制程序)与模型之间的集成。我们不需要关心模型底层的加载和推理细节。
  3. 活跃的社区和丰富的模型库:Ollama官方维护了一个模型库(ollama.com/library),里面有大量预量化好的热门模型,如Llama 3、Qwen、DeepSeek等,而且很多都提供了从1.5B到8B不等的多种尺寸,方便我们根据硬件能力选择。
  4. 对ARM平台的支持:Ollama提供了Linux ARM64的安装包,这是能在Jetson上运行的前提。

当然,Ollama也不是唯一选择。像llama.cpptext-generation-webui等也是优秀的本地部署方案。但Ollama在易用性和API标准化方面做得最好,特别适合我们这种需要将LLM能力快速集成到另一个系统(机器人系统)中的场景。

2.3 语音处理流水线设计

一个完整的“语音指令->机器人动作”流程,需要以下几个步骤:

  1. 语音采集:通过USB麦克风或reComputer Mini的音频接口采集音频流。
  2. 语音识别(ASR):将音频流实时转换为文本。这里也需要一个本地、轻量级的模型。我选择了OpenAI开源的Whisper模型的“tiny”或“base”版本。虽然Whisper本身不算特别小,但其tiny版本(约39M参数)在Orin Nano上实时运行是可行的,准确度对于近场、指令式语音也足够。
  3. 文本理解与生成(LLM):将识别出的文本指令,送入本地部署的Ollama服务中的小语言模型。这个模型需要完成两项任务:一是理解用户的自然语言指令(如“请轻轻拿起桌上的杯子”),二是将其转换成结构化的、Reachy Mini SDK能够理解的命令或参数(如arm.move_to(x, y, z)gripper.open())。这里涉及到“提示词工程”(Prompt Engineering),我们需要精心设计一个系统提示词(System Prompt),告诉LLM它现在是一个机器人控制专家,并且输出格式必须是固定的JSON。
  4. 指令执行:我们的主控程序(用Python编写)解析LLM输出的JSON,调用Reachy Mini的Python SDK(reachy-sdk)来执行相应的动作。

整个流水线的瓶颈很可能会在ASR和LLM推理环节。因此,模型选型和量化等级的选择至关重要。

3. 实战部署:一步步搭建本地语音LLM系统

理论说完了,接下来是实操环节。我会尽量详述每一步,包括可能遇到的坑和解决办法。

3.1 基础环境准备:在reComputer Mini上安装Ollama

reComputer Mini预装了Ubuntu 20.04或22.04。首先通过SSH连接到设备。

第一步:安装OllamaOllama提供了便捷的一键安装脚本。但由于网络原因,直接从官网下载可能会非常慢甚至失败。这里就需要用到国内镜像源,这是第一个实战技巧。

# 方法一:使用中科大镜像源加速安装(推荐) curl -fsSL https://ollama.com/install.sh | OLLAMA_HOST=https://mirrors.ustc.edu.cn/ollama sh

这条命令的关键在于设置OLLAMA_HOST环境变量,指向中科大的镜像站,这样下载安装包和后续拉取模型都会走国内CDN,速度有质的飞跃。

安装完成后,启动Ollama服务并设置为开机自启:

# 启动服务 sudo systemctl start ollama # 设置开机自启 sudo systemctl enable ollama # 查看服务状态 sudo systemctl status ollama

如果状态显示为active (running),说明服务启动成功。

第二步:验证安装并运行第一个模型Ollama服务启动后,默认会在本地11434端口提供一个API服务。我们先拉取一个超小模型进行测试,比如TinyLlama(约1.1B参数)。

# 拉取模型(同样会走镜像源,速度较快) ollama pull tinyllama # 运行模型并与它交互 ollama run tinyllama

在出现的提示符后,输入Hello,看看它是否能正常回复。这一步是为了验证Ollama基础功能是否正常。

一个关键配置:修改Ollama模型存储路径默认情况下,Ollama会把模型下载到~/.ollama/models目录。对于Jetson设备,其系统盘空间通常有限。我们可以将其修改到挂载的大容量SD卡或USB存储设备上。

  1. 首先,查看Ollama的服务配置文件:
    sudo systemctl edit ollama.service
  2. 这会打开一个临时编辑器,在其中添加以下内容,将/path/to/your/large/disk替换为你的大容量存储路径:
    [Service] Environment="OLLAMA_MODELS=/path/to/your/large/disk/ollama-models"
  3. 保存退出后,重新加载systemd配置并重启Ollama:
    sudo systemctl daemon-reload sudo systemctl restart ollama
  4. 之后所有通过ollama pull下载的模型都会存储在新的路径下。

3.2 语音识别模块:集成本地Whisper

Ollama负责LLM,我们还需要一个ASR引擎。这里选择faster-whisper,它是Whisper的一个CTranslate2实现,推理速度更快,内存占用更少,非常适合边缘设备。

安装faster-whisper及其依赖:

# 更新包列表 sudo apt update # 安装Python3和pip(如果尚未安装) sudo apt install python3-pip -y # 安装faster-whisper pip3 install faster-whisper

在Jetson ARM平台上,直接pip安装faster-whisper可能会因为需要编译一些C++依赖而失败。一个更稳妥的方法是使用NVIDIA JetPack SDK中已经优化过的库,或者安装预编译的wheel包。如果遇到困难,可以回退到使用原始的openai-whisper包(pip install openai-whisper),虽然速度慢一点,但兼容性最好。

编写一个简单的语音识别脚本:创建一个Python文件asr_server.py,实现一个持续监听麦克风、进行实时语音识别的服务。

import queue import sounddevice as sd import numpy as np from faster_whisper import WhisperModel # 配置参数 MODEL_SIZE = "tiny" # 可选 tiny, base, small。tiny最快,base折中。 DEVICE = "cpu" # 在Orin Nano上,先用CPU。如果CUDA配置好可尝试"cuda" COMPUTE_TYPE = "int8" # 量化类型,降低内存和计算量 # 加载模型 print(f"Loading Whisper {MODEL_SIZE} model...") model = WhisperModel(MODEL_SIZE, device=DEVICE, compute_type=COMPUTE_TYPE) # 音频参数 SAMPLE_RATE = 16000 CHUNK_DURATION = 3 # 每次处理3秒的音频块 CHUNK_SIZE = int(SAMPLE_RATE * CHUNK_DURATION) audio_queue = queue.Queue() def audio_callback(indata, frames, time, status): """音频回调函数,将数据放入队列""" if status: print(f"Audio callback status: {status}") audio_queue.put(indata.copy()) def transcribe_audio(audio_data): """转录音频数据""" audio_np = np.concatenate(audio_data, axis=0).astype(np.float32).flatten() # 使用Whisper进行识别 segments, info = model.transcribe(audio_np, beam_size=5, language="zh") text = "".join([seg.text for seg in segments]).strip() return text # 开始录音流 print("开始监听,请说话...") stream = sd.InputStream(callback=audio_callback, channels=1, samplerate=SAMPLE_RATE, blocksize=CHUNK_SIZE) stream.start() try: while True: # 收集一定时长的音频 audio_chunks = [] for _ in range(int(2 / CHUNK_DURATION)): # 例如,收集2秒音频再识别 audio_chunks.append(audio_queue.get()) if audio_chunks: text = transcribe_audio(audio_chunks) if text: # 只有识别到内容才打印 print(f"识别结果: {text}") # 这里可以将text发送给LLM处理模块 except KeyboardInterrupt: print("\n停止监听") finally: stream.stop() stream.close()

这个脚本创建了一个简单的实时语音识别循环。你需要先通过pip install sounddevice安装音频库。注意,在Jetson上使用USB麦克风时,可能需要通过arecord -l查看设备ID,并在sd.InputStream中指定device参数。

3.3 LLM模型选型与提示词工程

这是项目的核心智能部分。我们需要一个足够小、足够快,但理解能力和指令跟随能力又不错的模型。

模型选择:经过测试,以下几个模型在Orin Nano 8GB上表现较为均衡(使用Ollama的q4_0q8_0量化版本):

  • Qwen2.5-1.5B-Instruct:通义千问的小尺寸版本,中文理解好,指令跟随能力强,响应速度快。
  • Llama 3.2-1B-Instruct:Meta出品,英文指令处理非常出色,代码生成和结构化输出能力在同尺寸中领先。
  • Phi-3-mini-3.8B:微软的模型,虽然参数稍大(3.8B),但因其卓越的“小身材大能量”特性,经过q4_0量化后仍可在Orin Nano上运行,且综合能力很强。

我最终选择了Qwen2.5-1.5B-Instruct,因为我们的指令主要是中文,且它对结构化JSON输出的遵循性很好。用Ollama拉取它:

ollama pull qwen2.5:1.5b-instruct-q4_0

设计系统提示词(System Prompt):提示词的质量直接决定了LLM能否输出我们想要的、可解析的指令。我们的目标是让LLM成为一个“机器人指令翻译官”。

创建一个robot_prompt.txt文件,内容如下:

你是一个机器人控制助手。你的任务是将用户的自然语言指令,翻译成Reachy Mini机器人可以执行的、结构化的JSON命令。 Reachy Mini机器人有以下可控制部件: - 左臂 (left_arm) - 右臂 (right_arm) - 头部 (head,可以转动) - 左夹爪 (left_gripper) - 右夹爪 (right_gripper) 可能的动作类型(action)包括: - `move_to`: 将机械臂末端移动到指定坐标[x, y, z](单位:米)。坐标系原点在机器人底座中心。 - `move_joints`: 控制机械臂的关节角度[j1, j2, j3, j4, j5, j6, j7](单位:弧度)。 - `look_at`: 控制头部看向某个坐标[x, y, z]。 - `open_gripper`: 打开夹爪。 - `close_gripper`: 闭合夹爪。 - `set_gripper_force`: 设置夹爪力度(单位:牛顿)。 你输出的必须是且仅是一个合法的JSON对象,格式如下: { "actions": [ { "part": "left_arm", // 或 right_arm, head, left_gripper, right_gripper "action": "move_to", // 动作类型 "parameters": { // 动作参数,根据action类型不同而不同 "x": 0.2, "y": 0.1, "z": 0.3 } } // ... 可以包含多个顺序执行的动作 ] } 如果用户的指令不明确、无法实现或存在安全风险(如让机器人伤害自己或他人),请在JSON中返回一个错误信息,格式如下: { "error": "具体的错误描述信息" } 现在,请处理用户的指令。

这个提示词明确了机器人的能力边界、输出格式,并加入了安全校验。我们将把这个提示词作为每次对话的“系统指令”发送给Ollama。

3.4 集成与主控程序编写

现在,我们需要编写一个主控Python程序,它需要做三件事:

  1. 从ASR模块获取文本指令。
  2. 将文本指令和系统提示词组合,发送给Ollama API。
  3. 解析Ollama返回的JSON,调用Reachy SDK执行动作。

首先,安装Reachy SDK和requests库:

pip3 install reachy-sdk requests

主控程序robot_llm_controller.py的核心逻辑如下:

import json import requests import time from reachy_sdk import ReachySDK from reachy_sdk.trajectory import goto # 假设我们从asr_server.py通过某种方式(如队列、Socket)获取文本 # 这里简化处理,从标准输入读取模拟指令 import sys class RobotLLMController: def __init__(self, ollama_host="http://localhost:11434", model_name="qwen2.5:1.5b-instruct-q4_0"): self.ollama_url = f"{ollama_host}/api/generate" self.model_name = model_name # 加载系统提示词 with open('robot_prompt.txt', 'r', encoding='utf-8') as f: self.system_prompt = f.read() # 连接到Reachy Mini(假设机器人IP是192.168.1.100) try: self.reachy = ReachySDK(host='192.168.1.100') print("成功连接到Reachy Mini") except Exception as e: print(f"连接Reachy失败: {e}") self.reachy = None def ask_llm(self, user_input): """向Ollama发送请求,获取结构化指令""" payload = { "model": self.model_name, "prompt": user_input, "system": self.system_prompt, # 关键:传入系统提示词 "stream": False, "options": { "temperature": 0.1, # 低温度,让输出更确定、更遵循格式 "num_predict": 150 # 限制生成长度,避免废话 } } try: response = requests.post(self.ollama_url, json=payload, timeout=30) response.raise_for_status() result = response.json() return result['response'] except requests.exceptions.RequestException as e: return json.dumps({"error": f"LLM请求失败: {e}"}) except (KeyError, json.JSONDecodeError) as e: return json.dumps({"error": f"LLM响应解析失败: {e}"}) def parse_and_execute(self, llm_response): """解析LLM的JSON响应并执行机器人动作""" try: command = json.loads(llm_response) except json.JSONDecodeError: print(f"无法解析LLM响应为JSON: {llm_response}") return if "error" in command: print(f"LLM返回错误: {command['error']}") # 可以在这里让机器人通过语音或灯光提示错误 return if "actions" not in command or not isinstance(command["actions"], list): print(f"响应中缺少有效的'actions'列表: {command}") return if self.reachy is None: print("未连接到机器人,仅模拟执行") # 模拟执行,打印动作 for action in command["actions"]: print(f"模拟执行: {action}") return # 实际执行动作 for action_spec in command["actions"]: part = action_spec.get("part") action = action_spec.get("action") params = action_spec.get("parameters", {}) try: robot_part = getattr(self.reachy, part, None) if robot_part is None: print(f"未找到机器人部件: {part}") continue if action == "move_to": # 注意:reachy_sdk的移动API可能需要使用goto或特定方法 # 这里是一个示例,实际API请查阅SDK文档 target_pos = [params.get('x', 0), params.get('y', 0), params.get('z', 0)] # 假设robot_part有goto方法(这是一个简化示例) # goto({robot_part: target_pos}, duration=2.0) print(f"移动 {part} 到位置 {target_pos}") elif action == "open_gripper": robot_part.open() print(f"打开 {part}") elif action == "close_gripper": robot_part.close() print(f"闭合 {part}") # ... 处理其他动作类型 else: print(f"不支持的动作类型: {action}") except Exception as e: print(f"执行动作 {action} 于 {part} 时出错: {e}") time.sleep(0.5) # 动作间短暂停顿 def run(self): """主循环:从输入获取指令,处理,执行""" print("机器人语音LLM控制器已启动。请输入指令(或从ASR模块获取):") while True: # 这里替换为从ASR模块获取文本,例如: # user_text = asr_module.get_latest_transcription() user_text = input("> ").strip() if user_text.lower() in ['quit', 'exit', 'q']: break if not user_text: continue print(f"用户指令: {user_text}") llm_output = self.ask_llm(user_text) print(f"LLM原始输出:\n{llm_output}") self.parse_and_execute(llm_output) if __name__ == "__main__": controller = RobotLLMController() controller.run()

这个程序是一个框架。你需要将获取用户输入的部分(input("> "))替换为与你的asr_server.py的实际集成,比如通过一个共享的线程安全队列(queue.Queue)或者一个简单的WebSocket/RPC接口。同时,执行机器人动作的部分需要根据reachy-sdk的实际API进行完善。

4. 性能调优与踩坑实录

把整个系统跑起来只是第一步,让它跑得“流畅可用”才是挑战的开始。以下是几个关键的调优点和我踩过的坑。

4.1 模型量化与推理速度的权衡

Ollama拉取的模型标签如q4_0q8_0就代表了量化等级。数字越小(如q4),模型体积越小,推理速度越快,但精度损失也越大,可能导致模型“智力下降”,输出胡言乱语或无法遵循指令格式。

实测对比(在Orin Nano上):

  • qwen2.5:1.5b-instruct-q4_0: 模型文件约900MB,推理速度约15 tokens/秒。大部分指令能正确理解并输出JSON,但偶尔会在JSON外生成多余的解释文字,需要我们在程序里做后处理(如用正则表达式提取第一个{}之间的内容)。
  • qwen2.5:1.5b-instruct-q8_0: 模型文件约1.5GB,推理速度约10 tokens/秒。输出格式更稳定,几乎不会出现格式错误,理解能力也稍强。
  • qwen2.5:1.5b-instruct-fp16: 模型文件约3GB,内存占用过高,在8GB设备上与其他服务(如Whisper)同时运行极易导致OOM(内存溢出),不推荐。

结论与建议:对于机器人指令这种对格式要求严苛、但语义相对简单的任务,q4_0量化在速度和内存占用上优势明显。为了应对其偶尔的格式错误,必须在主控程序中加入健壮的输出清洗和解析逻辑。例如:

import re def extract_json_from_response(llm_output): # 尝试匹配第一个完整的JSON对象 match = re.search(r'\{[^{}]*\}', llm_output) if match: try: return json.loads(match.group()) except json.JSONDecodeError: pass # 如果匹配失败,返回错误 return {"error": "LLM输出无法解析为JSON"}

4.2 内存管理:避免OOM杀手

Jetson Orin Nano 8GB的内存是共享的,GPU和CPU共用。同时运行Ollama(加载LLM)、Whisper(语音识别)、Python主程序以及Reachy SDK的通信模块,内存压力很大。

监控与优化手段:

  1. 使用tegrastats监控:NVIDIA提供了tegrastats工具,可以实时查看CPU、GPU、内存、功耗等信息。运行sudo tegrastats,重点关注RAMSWAP的使用情况。
  2. 为Ollama设置运行限制:可以通过修改Ollama的系统服务文件,限制其可用的CPU和内存。编辑/etc/systemd/system/ollama.service.d/override.conf(如果没有则创建):
    [Service] MemoryMax=4G # 限制Ollama进程最多使用4GB内存 CPUQuota=80% # 限制CPU使用率
    修改后执行sudo systemctl daemon-reload && sudo systemctl restart ollama
  3. 卸载不用的模型:Ollama默认会将最近使用的模型留在内存中。如果切换模型,记得用ollama rm <model-name>卸载旧的模型,释放内存。
  4. 优化Whisper模型:使用tiny版本而非base版本,并使用int8量化(compute_type='int8'),可以节省近百MB内存。

4.3 延迟优化:从语音到动作的流水线

整个系统的端到端延迟(从说完话到机器人开始动)是体验的关键。延迟主要来自三部分:ASR处理时间、LLM推理时间、机器人运动规划时间。

实测数据与优化:

  • ASR延迟:使用Whisper-tiny,处理3秒音频块,在Orin Nano的CPU上约需0.8-1.2秒。优化方向:使用更小的音频块(如1秒),但会降低识别精度;或者尝试使用GPU运行Whisper(device='cuda'),但这需要配置好CUDA和CTranslate2的GPU支持,可能会增加复杂性。
  • LLM推理延迟:对于一条简单的指令“拿起杯子”,Qwen2.5-1.5B-q4_0生成约50个token的JSON响应,需要3-4秒。这是最大的瓶颈。优化方向:
    • 提示词精简:尽可能缩短系统提示词的长度,减少不必要的描述。
    • 使用num_predict参数:在Ollama API请求中明确限制生成token的数量,避免模型“说废话”。
    • 考虑流式响应:Ollama API支持流式响应("stream": true)。虽然对于JSON输出,我们需要完整的响应才能解析,但流式可以让我们在模型生成完第一个}(JSON对象结束)后就立即开始解析和执行,而不是等整个响应结束,这可以节省一点时间。
  • 流水线并行:一个重要的技巧是,不要让程序串行地等待每一步完成。可以采用生产者-消费者模式:ASR模块持续识别,一旦识别出一句完整的话(例如检测到静音端点),就立刻放入队列;主控程序从队列中取指令,发送给LLM;在LLM推理的同时,机器人可以执行上一条指令的后续动作(如果安全的话)。这样可以将ASR和LLM的等待时间重叠一部分。

4.4 稳定性与错误处理

在实际演示中,最怕的就是机器人因为一个未处理的异常而“僵住”或者做出危险动作。

必须加入的健壮性设计:

  1. LLM输出格式校验:如前所述,必须有强大的JSON解析和fallback机制。
  2. 指令安全性过滤:在系统提示词中要求LLM检查安全风险是不够的。在主控程序中,必须对解析出的动作进行二次校验。例如,检查move_to的坐标是否在机器人的工作空间限位内;检查close_gripper的力度是否超过最大值。
  3. 超时与重试:对Ollama API的请求设置超时(如30秒),并加入重试逻辑(最多2-3次)。网络或模型加载可能出现瞬时问题。
  4. 看门狗(Watchdog):编写一个简单的看门狗线程,定期检查ASR、LLM服务以及机器人连接是否健康。如果某个服务卡死,可以尝试自动重启或进入安全模式(如让机器人回到初始姿态)。
  5. 急停机制:务必保留一个物理急停按钮或一个优先级最高的语音指令(如“停下!”),该指令直接发送给机器人底层控制器,绕过整个LLM流水线,确保在任何情况下都能立即停止所有运动。

5. 效果评估与未来展望

部署完成后,我进行了一系列测试。对于“抬起左臂”、“张开右手”、“看着我的手”这类简单明确的指令,系统反应时间在5-7秒左右(从说完到开始动),其中LLM推理占了大头。虽然离“实时”还有距离,但对于展示、教育或非紧急的辅助场景,已经具备了很强的可玩性和实用性。

更复杂的指令,如“用左手拿起你面前的蓝色积木,然后放到右边的盒子里”,LLM能够分解成多个动作(look_at定位,move_to接近,close_gripper抓取, 再次move_to移动,open_gripper释放),并输出一个包含多个动作对象的JSON数组。这证明了小模型在特定任务提示词的引导下,具备一定的任务规划能力。

未来的优化方向非常明确:

  1. 专用小型模型微调:现在的通用小模型是“万金油”。我们可以收集一批“人类指令-机器人动作序列”的配对数据,在Qwen2.5-1.5B这样的基座模型上进行LoRA微调。微调后的模型会极度擅长将特定领域的自然语言映射为结构化指令,格式错误率会大幅下降,推理速度也可能因为任务更专注而间接提升。
  2. 硬件加速探索:深入研究Jetson平台上的TensorRT-LLM或NVIDIA Triton Inference Server,将Ollama替换为这些工业级推理服务器,可能获得更好的性能和资源管理。
  3. 多模态输入:目前只用了语音。Reachy Mini有摄像头,完全可以接入一个本地部署的轻量级视觉语言模型(VLM),如MiniCPM-VQwen-VL的小尺寸版本。实现“看着这个红色的方块,把它推下去”这类需要视觉 grounding 的指令。
  4. 上下文记忆:让机器人能记住之前的对话和状态,实现多轮交互。这需要在主控程序中维护一个对话历史,并在每次请求时将其作为上下文传递给LLM。

这个项目从构思到实现,最大的感触是:在边缘设备上部署AI应用,永远是在性能、精度、功耗和成本之间做精巧的平衡。没有完美的方案,只有针对特定场景的最优解。通过Ollama这样的工具,我们得以将曾经需要庞大算力的LLM能力,塞进一个巴掌大的机器人“大脑”里,这本身就是一件充满成就感的事情。当你对着机器人说出指令,并看到它真的尝试去理解并执行时,那种感觉,远比调用一个云端API要来得真实和有趣得多。