diff --git a/.gitignore b/.gitignore index 923dba63..fa607b72 100644 --- a/.gitignore +++ b/.gitignore @@ -139,3 +139,4 @@ cython_debug/ *.iml model.pt tmp +.DS_Store diff --git a/Dockerfile b/Dockerfile index 03e281ee..cf709a71 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com # 第二阶段:生产镜像 FROM python:3.10-slim -WORKDIR /opt/xiaozhi-es32-server +WORKDIR /opt/xiaozhi-esp32-server # 从构建阶段复制虚拟环境 COPY --from=builder /opt/venv /opt/venv diff --git a/README.md b/README.md index d0d5f634..3bab83bc 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,15 @@ # 部署方式 -本项目支持docker快速部署和本地源码运行。如果您主要是想快速体验,推荐使用docker部署。如果想深入了解本项目,推荐本地源码运行。 +本项目支持docker快速部署、借助docker环境运行部署和本地源码运行三种方式。三种方式怎么选,每个人的情况不一样,以下是一些推荐。 + +如果您主要是想快速体验,推荐使用`方式一:docker快速部署`。 + +如果您已经安装docker,不想折腾环境,又想对代码进行DIY修改,推荐`方式二:借助docker环境运行部署`。 + +如果您对conda的使用得心应手,或想从零开始学习搭建环境,推荐`方式三:本地源码运行`。 + +如果你对小智的回答速度非常的敏感,不希望有丝丝的性能损失,推荐`方式三:本地源码运行`。 ## 方式一:docker快速部署 @@ -88,7 +96,7 @@ docker镜像已支持x86架构、arm64架构的CPU,支持在国产操作系统 ### 4. 修改配置文件 -修改刚才你下载的`config.yaml`文件,配置本项目所需的各种参数。默认的LLM使用的是`ChatGLMLLM`,你需要配置密钥,才能启动。 +修改刚才你下载的`config.yaml`文件,配置本项目所需的各种参数。默认的LLM使用的是`ChatGLMLLM`,你需要配置密钥,因为他们的模型,虽然有免费的,但是仍要去[官网](https://bigmodel.cn/usercenter/proj-mgmt/apikeys)注册密钥,才能启动。 默认的TTS使用的是`EdgeTTS`,这个无需配置,如果你需要更换成`豆包TTS`,则需要配置密钥。 @@ -140,7 +148,7 @@ dir 如果你能看到`config.yaml`文件,确确实实进入到了`项目目录`,接着执行以下命令: ``` -docker run -d --name xiaozhi-esp32-server --restart always --security-opt seccomp:unconfined -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-es32-server/config.yaml ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest +docker run -d --name xiaozhi-esp32-server --restart always --security-opt seccomp:unconfined -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-esp32-server/config.yaml ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest ``` 如果首次执行,可能需要几分钟时间,你要耐心等待他完成拉取。正常拉取完成后,你可以在命令行执行以下命令查看服务是否启动成功 @@ -166,6 +174,9 @@ docker logs -f xiaozhi-esp32-server 正常来说,假设你的ip是`192.168.1.25`,那么你的接口地址就是:`ws://192.168.1.25:8000`。这个信息很有用的,后面`编译esp32固件` 需要用到。 +请注意,你的接口地址是`websocket`协议的地址,你可以使用`apifox`等工具调试。但是不能直接用浏览器打开访问,如果用浏览器打开,日志会显示错误,会让你怀疑是否部署成功了。 + + 后期如果想升级版本,可以这么操作 1、备份好`config.yaml`文件,一些关键的配置到时复制到新的`config.yaml`文件里。 @@ -180,7 +191,58 @@ docker rmi ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest 3.按本教程重新来一遍 -## 方式二:本地源码运行 +## 方式二:借助docker环境运行部署 + +这个方法的原理,其实是和第一种方式类似。区别在于,第一种方式只要下载一个配置文件,而本方式要下载项目源码。 + +优点就是源码在你主机躺着,你可以使用本机的代码编辑器加载项目和修改源码。每次修改完项目,想要看效果,那就要重启docker。 + +缺点就要下载本项目的代码,还要下载模型文件,如果这个项目不常用,确实会占用您电脑的空间。 + +下载源码后,需要下载模型文件。 默认使用`SenseVoiceSmall`模型,进行语音转文字。因为模型较大,需要独立下载,下载后把`model.pt`文件放在`model/SenseVoiceSmall` +目录下。下面两个下载路线任选一个。 + +- 线路一:阿里魔塔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt) +- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码: + `qvna` + +下载模型后,需要按照`方式一:docker快速部署`修改配置文件`config.yaml` + +修改完配置后,打开命令行工具,`cd`进入到你的项目目录下,执行以下命令 + +``` +docker run -d --name xiaozhi-esp32-server --restart always --security-opt seccomp:unconfined \ + -p 8000:8000 \ + -v $(pwd):/opt/xiaozhi-esp32-server \ + ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest +``` + +``` +docker logs -f xiaozhi-esp32-server +``` + +如果你能看到,类似以下日志,则是本项目服务启动成功的标志。 + +``` +2025-xx-xx xx:51:59,492 - core.server - INFO - Server is running at ws://xx.xx.xx.xxx:8000 +2025-xx-xx xx:51:59,516 - websockets.server - INFO - server listening on 0.0.0.0:8000 +``` + +接下来,你就可以开始 `编译esp32固件`了,请往下翻,翻到编译`esp32固件`相关章节。那么由于你是用docker部署,你要自己查看自己本机电脑的ip是多少。 +正常来说,假设你的ip是`192.168.1.25`,那么你的接口地址就是:`ws://192.168.1.25:8000`。这个信息很有用的,后面`编译esp32固件` +需要用到。 + +请注意,你的接口地址是`websocket`协议的地址,你可以使用`apifox`等工具调试。但是不能直接用浏览器打开访问,如果用浏览器打开,日志会显示错误,会让你怀疑是否部署成功了。 + +后期,如果你修改了代码,想让新代码跑起来,可以用以下命令让docker容器重启: + +``` +docker restart xiaozhi-esp32-server +# 查看日志输出 +docker logs -f xiaozhi-esp32-server +``` + +## 方式三:本地源码运行 ### 1.安装基础环境 @@ -226,7 +288,7 @@ pip install -r requirements.txt ### 4.配置项目 -修改`config.yaml`文件,配置本项目所需的各种参数。默认的LLM使用的是`ChatGLMLLM`,你需要配置密钥,才能启动。 +修改`config.yaml`文件,配置本项目所需的各种参数。默认的LLM使用的是`ChatGLMLLM`,你需要配置密钥,因为他们的模型,虽然有免费的,但是仍要去[官网](https://bigmodel.cn/usercenter/proj-mgmt/apikeys)注册密钥,才能启动。 默认的TTS使用的是`EdgeTTS`,这个无需配置,如果你需要更换成`豆包TTS`,则需要配置密钥。 配置说明:这里是各个功能使用的默认组件,例如LLM默认使用`ChatGLMLLM`模型。如果需要切换模型,就是改对应的名称。 @@ -278,6 +340,8 @@ python app.py 其中上面的`ws://192.168.1.25:8000`就是本项目提供的接口地址了,当然你自己的机器和我的是不一样的,记得要找到自己的地址。 +请注意,你的接口地址是`websocket`协议的地址,你可以使用`apifox`等工具调试。但是不能直接用浏览器打开访问,如果用浏览器打开,日志会显示错误,会让你怀疑是否部署成功了。 + # 编译esp32固件 1. 下载`xiaozhi-esp32` @@ -308,6 +372,13 @@ config WEBSOCKET_URL Communication with the server through websocket after wake up. ``` +注意:你的地址是`ws://`开头,不是`wss://`开头,一定不要写错了。 + +注意:你的地址是`ws://`开头,不是`wss://`开头,一定不要写错了。 + +注意:你的地址是`ws://`开头,不是`wss://`开头,一定不要写错了。 + + 3. 设置编译参数 ``` @@ -343,6 +414,8 @@ python release.py 编译成功后,会在项目根目录下的`build`目录下生成固件文件`merged-binary.bin`。 这个`merged-binary.bin`就是要烧录到硬件上的固件文件。 +注意:如果执行到第二命令后,报了“zip”相关的错误,请忽略这个错误,只要`build`目录下生成固件文件`merged-binary.bin`,对你没有太大影响,请继续。 + 6. 烧录固件 将esp32设备连接电脑,使用chrome浏览器,打开以下网址 @@ -357,7 +430,7 @@ https://espressif.github.io/esp-launchpad/ ## 1、TTS 经常失败,经常超时 -建议:如果`EdgeTTS`经常失败,先检查一下是否用了梯子。 +建议:如果`EdgeTTS`经常失败,先检查一下是否用了梯子,如果用了梯子,请把梯子关了试试。 如果用的是`火山引擎的豆包TTS`经常失败,最好使用付费版本,因为他们的测试版本只有2个并发。 ## 2、我想通过小智控制电灯、空调、远程开关机等操作。 diff --git a/README_en.md b/README_en.md index abf42356..7f1e2ff6 100644 --- a/README_en.md +++ b/README_en.md @@ -156,7 +156,7 @@ If you can see the `config.yaml` file, you have indeed entered the `project dire command: ``` -docker run -d --name xiaozhi-esp32-server --restart always --security-opt seccomp:unconfined -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-es32-server/config.yaml ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest +docker run -d --name xiaozhi-esp32-server --restart always --security-opt seccomp:unconfined -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-esp32-server/config.yaml ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest ``` If executed for the first time, it may take several minutes, and you have to be patient to wait for it to complete the diff --git a/config.yaml b/config.yaml index de65c3ab..be05f592 100644 --- a/config.yaml +++ b/config.yaml @@ -62,6 +62,7 @@ LLM: ChatGLMLLM: # 定义LLM API类型 type: openai + # glm-4-flash 是免费的,但是还是需要注册填写api_key的 # 可在这里找到你的api key https://bigmodel.cn/usercenter/proj-mgmt/apikeys model_name: glm-4-flash url: https://open.bigmodel.cn/api/paas/v4/ diff --git a/core/handle/audioHandle.py b/core/handle/audioHandle.py index 7217178c..e3dadde0 100644 --- a/core/handle/audioHandle.py +++ b/core/handle/audioHandle.py @@ -35,11 +35,13 @@ async def handleAudioMessage(conn, audio): conn.asr_audio.clear() conn.reset_vad_states() + async def startToChat(conn, text): # 异步发送 stt 信息 - asyncio.create_task( + stt_task = asyncio.create_task( schedule_with_interrupt(0, send_stt_message(conn, text)) ) + conn.scheduled_tasks.append(stt_task) conn.executor.submit(conn.chat, text) @@ -52,7 +54,10 @@ async def sendAudioMessage(conn, audios, duration, text): conn.tts_start_speak_time = time.time() # 发送 sentence_start(每个音频文件之前发送一次) - await send_tts_message(conn, "sentence_start", text) + sentence_task = asyncio.create_task( + schedule_with_interrupt(base_delay, send_tts_message(conn, "sentence_start", text)) + ) + conn.scheduled_tasks.append(sentence_task) conn.tts_duration += duration @@ -60,10 +65,6 @@ async def sendAudioMessage(conn, audios, duration, text): for idx, opus_packet in enumerate(audios): await conn.websocket.send(opus_packet) - # 每个音频文件发送结束时,发送 sentence_end - if idx == len(audios) - 1: - await send_tts_message(conn, "sentence_end", text) - if conn.llm_finish_task and text == conn.tts_last_text: stop_duration = conn.tts_duration - (time.time() - conn.tts_start_speak_time) stop_task = asyncio.create_task( @@ -71,6 +72,7 @@ async def sendAudioMessage(conn, audios, duration, text): ) conn.scheduled_tasks.append(stop_task) + async def send_tts_message(conn, state, text=None): """发送 TTS 状态消息""" message = { @@ -85,6 +87,7 @@ async def send_tts_message(conn, state, text=None): if state == "stop": conn.clearSpeakStatus() + async def send_stt_message(conn, text): """发送 STT 状态消息""" stt_text = get_string_no_punctuation_or_emoji(text) diff --git a/docs/docker-build.md b/docs/docker-build.md index 0451ebd2..b1c760c4 100644 --- a/docs/docker-build.md +++ b/docs/docker-build.md @@ -15,7 +15,7 @@ docker rm xiaozhi-esp32-server cd /Users/hrz/myworkspace/docker-java-env/thirddata/ cp /Users/hrz/myworkspace/esp32/xiaozhi-esp32-server/config.yaml ./ -docker run -d --name xiaozhi-esp32-server --restart always -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-es32-server/config.yaml xiaozhi-esp32-server:local +docker run -d --name xiaozhi-esp32-server --restart always -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-esp32-server/config.yaml xiaozhi-esp32-server:local docker logs -f xiaozhi-esp32-server @@ -40,6 +40,6 @@ docker manifest push ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest 6、运行线上镜像 ``` cd /Users/hrz/myworkspace/docker-java-env/thirddata/ -docker run -d --name xiaozhi-esp32-server --restart always -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-es32-server/config.yaml ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest +docker run -d --name xiaozhi-esp32-server --restart always -p 8000:8000 -v $(pwd)/config.yaml:/opt/xiaozhi-esp32-server/config.yaml ccr.ccs.tencentyun.com/xinnan/xiaozhi-esp32-server:latest docker logs -f xiaozhi-esp32-server ``` \ No newline at end of file diff --git a/docs/images/wechat.jpg b/docs/images/wechat.jpg index 105232b6..9c8299cd 100644 Binary files a/docs/images/wechat.jpg and b/docs/images/wechat.jpg differ