add config

This commit is contained in:
朱潮 2025-09-26 13:11:14 +08:00
parent 0f15b5060b
commit a7876fb472
7 changed files with 389 additions and 11 deletions

115
SYSTEMD_SETUP.md Normal file
View File

@ -0,0 +1,115 @@
# Local Voice Systemd 服务配置指南
## 问题解决方案
### 核心问题
- **直接运行**: 使用PulseAudio自动转换16000Hz采样率 ✅
- **systemd运行**: 直接访问USB设备只支持48000/44100Hz ❌
### 解决策略
通过配置systemd服务访问用户会话的PulseAudio利用其采样率转换能力。
## 部署步骤
### 1. 上传文件到树莓派
```bash
scp local-voice.service local-voice-user.service deploy-service.sh test-audio.py zhuchaowe@192.168.101.212:~/Local-Voice/
```
### 2. 运行部署脚本
```bash
cd ~/Local-Voice
./deploy-service.sh
```
### 3. 测试音频访问
```bash
python3 test-audio.py
```
### 4. 启动服务 (推荐使用用户级服务)
```bash
# 用户级服务 (推荐)
systemctl --user start local-voice-user.service
systemctl --user enable local-voice-user.service
# 查看日志
journalctl --user -u local-voice-user.service -f
# 如果用户级服务不工作,尝试系统级服务
sudo systemctl start local-voice.service
sudo systemctl enable local-voice.service
# 查看系统服务日志
sudo journalctl -u local-voice.service -f
```
## 服务配置说明
### 系统级服务 (local-voice.service)
- 适用于传统systemd环境
- 添加了PulseAudio环境变量
- 包含音频设备权限
### 用户级服务 (local-voice-user.service)
- 推荐方案,更好的音频访问
- 自动继承用户会话环境
- 无需额外环境变量配置
## 故障排除
### 音频设备不可用
1. 检查pipewire-pulse服务:
```bash
systemctl --user status pipewire-pulse
systemctl --user start pipewire-pulse
```
2. 检查音频设备权限:
```bash
groups # 确认在audio组中
```
3. 测试直接运行:
```bash
python3 multiprocess_recorder.py --enable-nfc
```
### 权限问题
如果遇到权限错误确保用户在audio组中:
```bash
sudo usermod -a -G audio $USER
# 重新登录或重启系统
```
### 环境变量问题
如果用户级服务正常但系统级服务有问题,可能需要:
1. 检查环境变量是否正确设置
2. 确认pipewire-pulse服务在系统启动时运行
3. 考虑使用用户级服务作为主要方案
## 验证步骤
1. **环境测试**: `python3 test-audio.py`
2. **服务启动**: `systemctl --user start local-voice-user.service`
3. **日志监控**: `journalctl --user -u local-voice-user.service -f`
4. **功能测试**: 使用NFC卡片触发录音功能
## 备用方案
如果上述方案不工作,可以考虑:
### 方案A: 使用screen会话
```bash
screen -dmS local-voice python3 ~/Local-Voice/multiprocess_recorder.py --enable-nfc
screen -r local-voice # 查看输出
```
### 方案B: 使用cron启动
在crontab中添加:
```
@reboot cd ~/Local-Voice && python3 multiprocess_recorder.py --enable-nfc
```
### 方案C: 修改代码支持多种采样率
如果需要修改代码支持48000Hz采样率请告知。

View File

@ -22,7 +22,9 @@
"enable_llm": true,
"enable_tts": true,
"character": "libai",
"max_tokens": 50
"max_tokens": 50,
"enable_chat_memory": true,
"max_history_length": 5
},
"detection": {
"zcr_min": 2400,

View File

@ -91,6 +91,18 @@ class ControlSystem:
'failed_processing': 0
}
# 聊天历史记录
self.chat_history = []
# 从配置中读取聊天历史设置
self.enable_chat_memory = self.config.get('processing', {}).get('enable_chat_memory', True)
self.max_history_length = self.config.get('processing', {}).get('max_history_length', 5)
# 显示聊天记忆状态
if self.enable_chat_memory:
print(f"💬 聊天记忆功能已启用,最多保存 {self.max_history_length} 轮对话")
else:
print("💬 聊天记忆功能已禁用")
# 运行状态
self.running = True
@ -997,11 +1009,28 @@ class ControlSystem:
"Authorization": f"Bearer {self.api_config['llm']['api_key']}"
}
# 构建消息列表
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": text}
{
"role": "system",
"content": system_prompt
}
]
# 如果启用聊天记忆,添加历史对话记录
if self.enable_chat_memory:
for history_item in self.chat_history:
messages.extend([
{"role": "user", "content": history_item["user"]},
{"role": "assistant", "content": history_item["assistant"]}
])
# 添加当前用户消息
messages.append({
"role": "user",
"content": text
})
data = {
"model": self.api_config['llm']['model'],
"messages": messages,
@ -1020,7 +1049,14 @@ class ControlSystem:
result = response.json()
if 'choices' in result and len(result['choices']) > 0:
content = result['choices'][0]['message']['content']
return content.strip()
filtered_content = self._filter_parentheses_content(content.strip())
# 保存对话到历史记录
if self.enable_chat_memory:
self._add_to_chat_history(text, filtered_content)
print(f"💬 对话已保存到历史记录 (当前历史长度: {len(self.chat_history)})")
return filtered_content
print(f"❌ LLM API调用失败: {response.status_code}")
return None
@ -1059,13 +1095,23 @@ class ControlSystem:
{
"role": "system",
"content": system_prompt
},
{
"role": "user",
"content": text
}
]
# 如果启用聊天记忆,添加历史对话记录
if self.enable_chat_memory:
for history_item in self.chat_history:
messages.extend([
{"role": "user", "content": history_item["user"]},
{"role": "assistant", "content": history_item["assistant"]}
])
# 添加当前用户消息
messages.append({
"role": "user",
"content": text
})
data = {
"model": self.api_config['llm']['model'],
"messages": messages,
@ -1181,6 +1227,12 @@ class ControlSystem:
print(f"🎵 发送剩余句子: {filtered_sentence}")
self._send_streaming_text_to_output_process(filtered_sentence)
# 保存完整回复到历史记录
if accumulated_text and self.enable_chat_memory:
filtered_response = self._filter_parentheses_content(accumulated_text.strip())
self._add_to_chat_history(text, filtered_response)
print(f"💬 对话已保存到历史记录 (当前历史长度: {len(self.chat_history)})")
# 通知输出进程LLM生成已完成
self._notify_llm_complete()
@ -1651,6 +1703,10 @@ class ControlSystem:
print(f"🔄 角色已切换: {old_character} -> {character_name}")
# 清空聊天历史
self.clear_chat_history()
print(f"🔄 角色切换,聊天历史已清空")
# 播放新角色打招呼
print("🎭 播放新角色打招呼...")
greeting_success = self.play_greeting()
@ -1756,6 +1812,33 @@ class ControlSystem:
"current_character": self.nfc_manager.get_current_character(),
"running": self.nfc_manager.running
}
def _add_to_chat_history(self, user_message, assistant_response):
"""添加对话到历史记录"""
import time
# 如果历史记录超过最大长度,移除最早的记录
if len(self.chat_history) >= self.max_history_length:
self.chat_history.pop(0)
# 添加新的对话记录
self.chat_history.append({
"user": user_message,
"assistant": assistant_response,
"timestamp": time.time()
})
def clear_chat_history(self):
"""清空聊天历史"""
self.chat_history = []
print("💬 聊天历史已清空")
def get_chat_history_summary(self):
"""获取聊天历史摘要"""
if not self.chat_history:
return "暂无聊天历史"
return f"当前有 {len(self.chat_history)} 轮对话记录"
def main():
"""主函数"""

78
deploy-service.sh Executable file
View File

@ -0,0 +1,78 @@
#!/bin/bash
# Local Voice 服务部署脚本
# 用于解决systemd环境下的音频设备访问问题
echo "🔧 Local Voice 服务部署脚本"
echo "=================================="
# 获取用户ID
USER_ID=$(id -u)
USERNAME=$(whoami)
echo "👤 当前用户: $USERNAME (UID: $USER_ID)"
# 备份原有服务文件
echo "💾 备份原有服务文件..."
if systemctl list-unit-files | grep -q "local-voice.service"; then
sudo systemctl stop local-voice.service 2>/dev/null
sudo systemctl disable local-voice.service 2>/dev/null
sudo cp /etc/systemd/system/local-voice.service /etc/systemd/system/local-voice.service.backup 2>/dev/null || true
echo "✅ 原有服务已备份"
fi
# 安装systemd服务
echo "📦 安装systemd服务..."
sudo cp local-voice.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable local-voice.service
echo "✅ 系统级服务已安装"
# 安装用户级服务
echo "🏠 安装用户级服务..."
mkdir -p ~/.config/systemd/user/
cp local-voice-user.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable local-voice-user.service
echo "✅ 用户级服务已安装"
# 确保pipewire-pulse运行
echo "🎵 检查音频服务..."
if ! systemctl --user is-active --quiet pipewire-pulse; then
echo "🔧 启动pipewire-pulse服务..."
systemctl --user start pipewire-pulse
systemctl --user enable pipewire-pulse
echo "✅ pipewire-pulse已启动"
else
echo "✅ pipewire-pulse正在运行"
fi
# 启用用户服务 lingering (允许用户服务在登出后继续运行)
echo "🔄 启用用户服务lingering..."
sudo loginctl enable-linger $USERNAME
echo "✅ 用户服务lingering已启用"
# 测试建议
echo ""
echo "🎯 测试建议:"
echo "1. 先测试用户级服务: systemctl --user start local-voice-user.service"
echo "2. 查看用户服务日志: journalctl --user -u local-voice-user.service -f"
echo "3. 如果用户级服务正常,再考虑使用系统级服务"
echo ""
echo "🔍 故障排除:"
echo "- 如果用户级服务正常: 建议使用 systemctl --user start local-voice-user.service"
echo "- 如果系统级服务正常: 使用 sudo systemctl start local-voice.service"
echo ""
echo "📊 当前音频设备状态:"
python3 -c "
import pyaudio
p = pyaudio.PyAudio()
print('可用音频设备:')
for i in range(p.get_device_count()):
info = p.get_device_info_by_index(i)
if info['maxInputChannels'] > 0:
print(f' 设备 {i}: {info[\"name\"]} (默认采样率: {info[\"defaultSampleRate\"]}Hz)')
p.terminate()
"
echo ""
echo "✅ 部署完成!"

16
local-voice-user.service Normal file
View File

@ -0,0 +1,16 @@
[Unit]
Description=Local Voice Multiprocess Recorder with NFC (User Service)
After=network.target sound.target pipewire-pulse.service
Wants=network.target sound.target pipewire-pulse.service
[Service]
Type=simple
WorkingDirectory=/home/zhuchaowe/Local-Voice
ExecStart=/usr/bin/python3 /home/zhuchaowe/Local-Voice/multiprocess_recorder.py --enable-nfc
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target

View File

@ -1,17 +1,24 @@
[Unit]
Description=Local Voice Multiprocess Recorder with NFC
After=network.target
Wants=network.target
After=network.target sound.target
Wants=network.target sound.target
[Service]
Type=simple
User=zhuchaowe
WorkingDirectory=/home/zhuchaowe
Group=audio
WorkingDirectory=/home/zhuchaowe/Local-Voice
Environment="PULSE_SERVER=unix:/run/user/%U/pulse/native"
Environment="XDG_RUNTIME_DIR=/run/user/%U"
Environment="DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus"
ExecStart=/usr/bin/python3 /home/zhuchaowe/Local-Voice/multiprocess_recorder.py --enable-nfc
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# 添加设备访问权限
DeviceAllow=/dev/snd/*
DevicePolicy=auto
[Install]
WantedBy=multi-user.target

77
test-audio.py Executable file
View File

@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
音频设备测试脚本
用于测试不同环境下的音频设备访问
"""
import pyaudio
import sys
import os
def test_audio_access():
print("🔍 音频设备访问测试")
print("=" * 50)
try:
p = pyaudio.PyAudio()
print("✅ PyAudio 初始化成功")
# 显示所有音频设备
print("\n📋 可用音频设备:")
for i in range(p.get_device_count()):
info = p.get_device_info_by_index(i)
if info['maxInputChannels'] > 0:
print(f" 设备 {i}: {info['name']}")
print(f" 输入通道: {info['maxInputChannels']}")
print(f" 默认采样率: {info['defaultSampleRate']}")
# 测试16000Hz采样率
print(f"\n🎵 测试16000Hz采样率访问:")
# 测试默认设备
try:
stream = p.open(
format=pyaudio.paInt16,
channels=1,
rate=16000,
input=True,
frames_per_buffer=1024
)
print("✅ 默认设备支持16000Hz")
stream.close()
except Exception as e:
print(f"❌ 默认设备不支持16000Hz: {e}")
# 测试pulse设备
try:
stream = p.open(
format=pyaudio.paInt16,
channels=1,
rate=16000,
input=True,
frames_per_buffer=1024,
input_device_index=6 # pulse设备
)
print("✅ Pulse设备支持16000Hz")
stream.close()
except Exception as e:
print(f"❌ Pulse设备不支持16000Hz: {e}")
p.terminate()
print("\n✅ 音频测试完成")
except Exception as e:
print(f"❌ 音频测试失败: {e}")
sys.exit(1)
def check_environment():
print("\n🌍 环境信息:")
print(f"用户: {os.getenv('USER', 'Unknown')}")
print(f"用户ID: {os.getuid()}")
print(f"PULSE_SERVER: {os.getenv('PULSE_SERVER', 'Not set')}")
print(f"XDG_RUNTIME_DIR: {os.getenv('XDG_RUNTIME_DIR', 'Not set')}")
print(f"DBUS_SESSION_BUS_ADDRESS: {os.getenv('DBUS_SESSION_BUS_ADDRESS', 'Not set')}")
if __name__ == "__main__":
check_environment()
test_audio_access()