1. 项目概述
Verdure Assistant(绿荫助手)是一个基于.NET 9.0的多平台AI语音助手项目,旨在为开发者提供一个功能完善、易于扩展的智能对话机器人框架。这个开源项目支持Windows桌面、Android移动端、命令行以及Web API等多种使用方式,能够满足不同场景下的语音交互需求。
作为一名长期从事智能硬件开发的工程师,我在完成机器人硬件控制部分的开发后(包括屏幕控制、舵机驱动等功能),发现对话体验还有很大提升空间。受ESP32社区热门的小智AI机器人启发,我决定重新实现一个.NET版本的小智客户端,并将其打造成一个完整的开源项目。
提示:项目代码已完全开源,GitHub地址为 https://github.com/maker-community/Verdure.Assistant
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心架构设计
2.1 分层架构解析
项目采用清晰的分层架构设计,各模块职责明确,便于维护和扩展:
code复制Verdure.Assistant/
├── src/ # 源代码目录
│ ├── Verdure.Assistant.Core/ # 核心功能库(音频处理、网络通信等)
│ ├── Verdure.Assistant.ViewModels/ # 共享视图模型(MVVM模式)
│ ├── Verdure.Assistant.Console/ # 命令行应用
│ ├── Verdure.Assistant.WinUI/ # WinUI桌面应用
│ ├── Verdure.Assistant.MAUI/ # MAUI跨平台移动应用
│ └── Verdure.Assistant.Api/ # Web API服务
├── tests/ # 单元测试
├── docs/ # 项目文档
└── scripts/ # 构建脚本
这种架构设计有以下几个优势:
- 核心功能与界面展示分离,便于复用
- 各平台专用代码独立存放,避免交叉污染
- 测试代码与实现代码分离,保证质量
2.2 关键技术选型
项目采用了以下核心技术栈:
- 核心框架:.NET 9.0(跨平台基础)
- UI框架:
- WinUI 3(Windows桌面)
- .NET MAUI(移动端)
- 音频处理:
- OpusSharp(音频编解码)
- SoundFlow(音频录制与播放)
- 网络通信:
- WebSocket(实时对话)
- MQTT(设备控制,待完善)
- AI服务:小智AI(当前主要对接服务)
选择这些技术主要基于以下考虑:
- .NET 9.0提供了优秀的跨平台能力
- MAUI可以一套代码适配多个移动平台
- Opus编解码在语音场景下表现优异
- SoundFlow是新出现的优质音频库,功能完善
3. 核心模块实现
3.1 语音交互模块
语音交互是项目的核心功能,主要包括以下几个部分:
3.1.1 关键词唤醒
项目实现了离线关键词唤醒功能,支持"你好小电"和"你好小娜"两种唤醒词。核心代码如下:
csharp复制public async Task StartAsync(IAudioRecorder? audioRecorder = null)
{
if (!_isEnabled) return;
try {
await _semaphore.WaitAsync();
_cancellationTokenSource = new CancellationTokenSource();
// 设置音频源
if (audioRecorder != null) {
_audioRecorder = audioRecorder;
_useExternalAudioSource = true;
}
// 加载关键词模型
if (!await LoadKeywordModelsAsync()) {
throw new Exception("加载关键词模型失败");
}
// 配置音频输入
var audioConfig = await ConfigureSharedAudioInput();
_keywordRecognizer = new KeywordRecognizer(audioConfig);
// 启动识别
_ = Task.Run(async () => {
await _keywordRecognizer.RecognizeOnceAsync(_keywordModel);
});
}
catch (Exception ex) {
_logger?.LogError(ex, "关键词识别启动失败");
}
}
实现要点:
- 支持外部音频源接入,灵活性高
- 使用信号量控制并发访问
- 后台任务执行识别,不阻塞主线程
3.1.2 音频处理流水线
音频处理采用生产者-消费者模式,通过Channel实现高效数据传输:
csharp复制public class AudioDataDistributor : IAudioDataDistributor
{
private readonly Channel<byte[]> _audioChannel;
private readonly ILogger? _logger;
public AudioDataDistributor(ILogger? logger = null)
{
_logger = logger;
var options = new UnboundedChannelOptions {
SingleReader = true,
SingleWriter = false,
AllowSynchronousContinuations = false
};
_audioChannel = Channel.CreateUnbounded<byte[]>(options);
}
public async Task WriteAsync(byte[] data)
{
await _audioChannel.Writer.WriteAsync(data);
}
public IAsyncEnumerable<byte[]> ReadAllAsync()
{
return _audioChannel.Reader.ReadAllAsync();
}
}
这种设计的好处是:
- 无界通道避免数据丢失
- 单读者多写者模式适合音频场景
- 异步流式处理提高吞吐量
3.2 状态管理模块
3.2.1 会话状态机
项目使用状态机管理对话流程,主要状态包括:
- IDLE(空闲):等待唤醒
- LISTENING(监听):正在录音
- SPEAKING(说话):播放回复
状态转换核心逻辑:
csharp复制public bool RequestTransition(ConversationTrigger trigger, string? context = null)
{
lock (_stateLock) {
var toState = GetNextState(_currentState, trigger);
if (toState == null) return false;
_logger?.LogInformation("状态转换: {FromState} -> {ToState}",
_currentState, toState.Value);
_previousState = _currentState;
_currentState = toState.Value;
StateChanged?.Invoke(this, new StateTransitionEventArgs {
FromState = _previousState,
ToState = _currentState,
Trigger = trigger,
Context = context
});
return true;
}
}
状态机的优势:
- 明确的状态边界,避免条件判断混乱
- 线程安全的状态转换
- 完善的状态变更事件通知
3.2.2 状态处理逻辑
每个状态都有对应的进入和退出处理:
csharp复制private void InitializeStateMachine()
{
_stateMachineContext = new ConversationStateMachineContext {
OnEnterListening = async () => {
await StartListeningInternalAsync();
},
OnExitSpeaking = async () => {
await StopSpeakingInternalAsync();
},
// 其他状态回调...
};
}
这种设计使得:
- 状态逻辑集中管理
- 异步操作支持良好
- 易于扩展新状态
4. 音频处理技术细节
4.1 Opus编解码实现
项目使用OpusSharp进行音频编解码,主要特点:
- 低延迟(适合实时语音)
- 高压缩率(节省带宽)
- 优秀的语音质量
编码器实现关键点:
csharp复制public byte[] Encode(byte[] pcmData, int sampleRate, int channels)
{
lock (_lock) {
// 参数校验
if (sampleRate != 16000 || channels != 1) {
_logger?.LogWarning("非标准音频参数");
}
// 初始化编码器(按需创建)
if (_encoder == null || _currentSampleRate != sampleRate) {
_encoder?.Dispose();
_encoder = new OpusEncoder(sampleRate, channels,
OpusPredefinedValues.OPUS_APPLICATION_AUDIO);
}
// 计算帧大小(60ms)
int frameSize = sampleRate * 60 / 1000;
// 执行编码
short[] pcmShorts = ConvertToShortArray(pcmData, frameSize);
byte[] outputBuffer = new byte[4000];
int encodedLength = _encoder.Encode(pcmShorts, frameSize,
outputBuffer, outputBuffer.Length);
// 返回结果
byte[] result = new byte[encodedLength];
Array.Copy(outputBuffer, result, encodedLength);
return result;
}
}
编码过程注意事项:
- 确保音频参数一致性
- 合理的帧大小计算(60ms)
- 线程安全操作
- 资源及时释放
4.2 SoundFlow音频框架
SoundFlow是项目使用的跨平台音频框架,主要功能:
- 统一音频接口
- 低延迟播放/录制
- 设备自动发现
播放器初始化代码:
csharp复制private void InitializeAudioEngine()
{
try {
_engine = new MiniAudioEngine();
// 设备枚举(调试用)
if (_logger?.IsEnabled(LogLevel.Debug) ?? false) {
_logger.LogDebug("可用播放设备:");
foreach (var device in _engine.PlaybackDevices) {
_logger.LogDebug("{Name}{Default}",
device.Name, device.IsDefault ? " (默认)" : "");
}
}
}
catch (Exception ex) {
_logger?.LogError(ex, "音频引擎初始化失败");
throw;
}
}
使用SoundFlow的注意事项:
- 设备初始化可能失败,需要异常处理
- 不同平台可能有不同的默认设备
- 采样率等参数需要统一配置
5. 多平台适配指南
5.1 Windows桌面版(WinUI)
开发环境要求:
- Windows 10/11
- Visual Studio 2022 (17.8+)
- .NET 9.0 SDK
关键实现点:
- 使用WinUI 3构建现代化界面
- 深色/浅色主题支持
- 系统托盘图标集成
5.2 Android移动版(MAUI)
配置要点:
xml复制<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
常见问题解决:
- 录音权限被拒绝:引导用户手动开启
- 后台录音限制:使用前台服务
- 不同设备兼容性:测试多种机型
5.3 命令行版
典型使用场景:
bash复制# 启动控制台应用
dotnet run --project src/Verdure.Assistant.Console
# 指定日志级别
dotnet run -- --logLevel Debug
功能特点:
- 适合服务器部署
- 详细的日志输出
- 脚本集成友好
5.4 Web API服务
主要API端点示例:
csharp复制// 音乐搜索
app.MapGet("/api/music/search", (string songName) => {
return _musicService.Search(songName);
});
// 播放控制
app.MapPost("/api/music/play", (SongRequest request) => {
return _musicService.Play(request);
});
部署建议:
- 使用Nginx反向代理
- 配置HTTPS加密
- 启用身份验证
6. 常见问题与解决方案
6.1 音频质量问题
症状:声音断断续续或失真
可能原因:
- 采样率不匹配
- 缓冲区大小不合适
- 编解码参数错误
解决方案:
- 统一使用16kHz采样率
- 调整缓冲区大小为60ms帧
- 检查Opus编解码配置
6.2 唤醒词不灵敏
优化方向:
- 检查麦克风权限
- 调整音频增益
- 测试不同环境噪音条件
调试方法:
csharp复制// 启用详细日志
_logger.LogDebug("音频数据: {Length}字节, RMS: {Rms}",
audioData.Length, CalculateRms(audioData));
6.3 跨平台兼容性问题
已知问题:
- Android后台限制
- iOS音频权限差异
- Linux音频设备发现
应对策略:
- 平台特定代码隔离
- 功能降级方案
- 充分的真机测试
7. 项目扩展与未来计划
7.1 插件系统设计
计划实现的扩展点:
- 技能插件(天气、音乐等)
- AI服务适配器
- 设备控制协议
示例接口:
csharp复制public interface IAssistantPlugin
{
string Name { get; }
Task<string> ExecuteAsync(string command);
bool CanHandle(string intent);
}
7.2 更多AI服务集成
待接入服务:
- OpenAI ChatGPT
- 百度UNIT
- 阿里云NLP
设计考虑:
- 统一的对话接口
- 服务自动切换
- 配额管理
7.3 硬件集成优化
计划支持:
- 树莓派GPIO控制
- 传感器数据接入
- 自定义执行器
实现方式:
csharp复制public interface IHardwareController
{
Task<bool> InitializeAsync();
Task SendCommandAsync(string command);
event EventHandler<HardwareEvent> EventReceived;
}
在实际开发中,我发现良好的架构设计可以大幅降低后续维护成本。特别是在跨平台项目中,清晰的层次划分和接口定义尤为重要。建议开发者在实现功能前,先花时间设计好模块边界和通信机制。
