跳转至

概述*

文档简要介绍 GX8006 WiFi 语音大模型方案的SDK获取步骤及开发环境搭建方法,助您快速开始开发。

1. 获取SDK*

注册GitLab

GX8006 WiFi 语音大模型方案的 SDK 部署在我司的 GitLab 服务器上,开发者需要注册后才可拉取相关代码进行二次开发,注册教程参考 注册GitLab

注册完毕并且通过审核,将会在群组 Voice WiFi Solution 下看到以下两个 SDK:

  • lighting :基于 LN882H WiFi 芯片开发的大模型方案 SDK
  • ovp_aiot :基于 GX8006 语音芯片开发的大模型方案 SDK

2. 搭建开发环境*

2.1. ovp_aiot 开发环境搭建*

ovp_aiot 的开发环境搭建详见 GX8006 SDK 开发环境搭建

2.2. lighting 开发环境搭建*

注意

当前 SDK 仅维护 CMake + GCC 的构建方式,SDK 中的 Keil 工程文件为亮牛 SDK 保留,并不支持直接编译,用户如果需要使用 Keil 构建项目,需要自行参考 project 下的 CMakeLists.txt 添加相关源码到工程中

2.2.1 相关软件说明*

  • Python3:构建中使用 python 脚本,且SDK路径不能包含中文,不支持 Python2
  • ARM GCC 工具链:选用ARM官方的 GNU Arm Embedded Toolchain: 10-2020-q4-major 版本
  • CMake:根据所选生成器生成对应的 Makefile 文件或者 build.ninja 文件。推荐版本 >= 3.16
  • Ninja:构建工具,处理 CMake 生成的 build.ninja 文件
  • Make:读取 CMake 生成的 Makefile 文件,调用编译器套件生成目标
  • SEGGER JLink:可选,用于烧录固件,也可以启动GDB调试服务器,推荐安装 V752d 及以下版本
  • 亮牛串口烧录工具:可选,用于从串口烧录固件,相关资料参考 LN882H文档资料集合
  • Visual Studio Code:可选

提示

以上软件根据需要选择安装

2.2.2. 基于 Ubuntu 20.04 x64 的安装说明*

  • 使用包管理器安装必要依赖软件
    • sudo apt install python3 cmake ninja-build make
  • 安装交叉编译工具链
    • 点击链接下载 gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
    • 解压工具链, sudo tar -xf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 -C /opt
    • 配置环境变量 export CROSS_TOOLCHAIN_ROOT=/opt/gcc-armnone-eabi-10-2020-q4-major,可添加到自定义脚本或 ./.bashrc 否则每次打开新的 shell 环境都需要配置该环境变量
  • SEGGER JLink(可选)

2.2.3. 基于 VSCode+GDB 的调试环境搭建说明*

  • 完成上述 基于 Ubuntu 20.04 x64 的安装
  • 自行安装VSCode及以下插件
    • C/C++ IntelliSense
    • CMake
    • CMake Tools
    • Cortex-Debug
  • 将 JLink 调试器连接PC,测试调试器是否连接:
    (base) zhuhy@zhuhy-lenovo:~$ lsusb
    Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 002: ID 1366:0101 SEGGER J-Link PLUS
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    
  • 打开 SDK 根目录下的 CMakeLists.txt,找到如下区域,修改编译类型为Debug
    # set(CMAKE_BUILD_TYPE  Release CACHE  STRING  "build for release"    FORCE)
    set(CMAKE_BUILD_TYPE  Debug   CACHE  STRING  "build for debug"      FORCE)
    
  • 重新编译并烧录 build-ln_model_public-debug 下的固件到 LN882H
  • 新建 .vscode/launch.json 内容如下,其中 serverpath 修改为 JLink 安装的路径,armToolchainPath 修改为工具链安装的路径
    {
        // 使用 IntelliSense 了解相关属性。 
        // 悬停以查看现有属性的描述。
        // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Cortex Debug",
                "type": "cortex-debug",
    
                "cwd": "${workspaceFolder}",
                "executable": "${workspaceFolder}/build-ln_model_public-debug/bin/ln_model_public.elf",
    
                "request": "attach",
                "runToEntryPoint": "main",
                "showDevDebugOutput": "both",
    
                "servertype": "jlink",
                "serverpath": "/usr/bin/JLinkGDBServerCLExe",
    
                "device": "Cortex-M4",
                "interface": "swd",
                "armToolchainPath": "/opt/gcc-arm-none-eabi-10-2020-q4-major/bin" 
            }
        ]
    }
    
  • 开启调试即可在 VSCode 的 UI 界面上进行单步调试

3. 开发指南*

3.1. ovp_aiot 开发指南*

正在开发中

3.2. lighting 开发指南*

3.2.1. 框架介绍*

WiFi 语音大模型的开发方案位于 project/ln_model_public

本方案主要包含三大块:

一是云端对接,即和云端之间的音频双向交互,以及一些控制消息的传输

二是和GX8006语音芯片的交互,即基于串口的双向音频传输及流控,相关控制指令的发送,例如配置播放参数、设置音量等

其次,基于小智云,给出了一套完整的授权和OTA,以及基于MCP实现控制的示例

3.3.2. 云端对接*

云端对接的源码位于 app/servers

  • 云端对接抽象出一份接口 app/servers/server_interface.h,对接不同的云端需要实现这份接口的函数定义,
  • 和云端的交互被抽象为:
    • 连接和断开,以及对应的已连接的回调
    • 音频的上传,即实现接收应用层其他模块传入的用户的语音数据,打包成云端指定的格式,推送给云端
    • 音频的下载,即实现云端的tts等数据的下载,通过回调函数传递给应用层其他模块
    • 相关控制信息的传递,例如音频的启停消息、唤醒词信息、MCP控制消息等

3.3.3. 串口对接GX8006*

串口对接的源码位于 app/smartbot

  • 模块向底层提取出两个接口,即一收一发;此部分相对固定,若需要移植请参考 串口协议移植指南
    • smartbot_on_output 用于注册串口发送函数,模块内部调用该函数向串口输出字节流
    • smartbot_proto_input 用于传入串口接收字节流,模块内部会解析数据帧,

    注意

    此接口会短暂关闭中断,用于向 ringbuffer 拷贝数据,但其后的协议解析行为在线程上下文中进行

  • 模块向应用层提取出两个播放接口,分别用于播放本地音频和云端音频
    • 本地音频存储于 Flash 代码段,播放时仅提供对应 ID,本模块会自动推送相对应的音频
    • 云端音频缓冲在 RAM 区域,播放时云端模块将数据传入该缓冲,本模块自动处理和GX8006的串口交互
    • 本地播报优先级高于云端播报
    • 本模块的整体框架示意图如下:

WiFi Smartbot 串口对接框架示意图

3.2.4. 授权管理*

提示

当前以小智的授权管理为例,不同云端请根据实际情况进行实现

本方案的授权管理有两个步骤

  • 步骤一:
    • 出厂时,通过向授权服务器POST请求,附带自身唯一标识
    • 解析返回的数据,提取加密秘钥和唯一标识
    • 本地OTP或Flash特殊区域写入秘钥
  • 步骤二:
    • 用户端启动设备后,检查自身是否有合法的秘钥,否则不执行后续应用代码
    • 通过向服务器POST请求,附带经秘钥加密的challenge信息,由服务器判断是否授权
    • 若设备是一个新设备,还未绑定过,服务器返回绑定验证码,用户打开控制台即可绑定设备
    • 若设备已绑定,则云端返回交互的 MQTT 连接参数等,设备连接后可建立和云端的交互

3.2.5. OTA*

由于本方案包含两颗芯片,因此OTA部分分为两部分,GX8006 部分和 LN882H 部分

在本 OTA 方案中,LN882H 作为主机,主导两颗芯片的升级工作

3.2.5.1. GX8006*

GX8006 OTA 通过和 LN882H 连接的串口实现裸片升级,即和开发时PC上位机通过串口烧录固件的原理一致,本方案实现了基于 HTTP 的流式 OTA

OTA 的源码位于 project/ln_model_public/app/ota ,GX8006 的裸片升级协议实现位于 project/ln_model_public/modules/gx_fornax_boot,

裸片升级的具体流程可参考 串口裸机升级参考例程

3.2.5.2. LN882H*

正在开发中

3.2.6. MCP*

小智的 MCP 实现在 project/ln_model_public/app/servers/xiaozhi/mcp.c 中,目前添加了获取设备状态信息和设置音量的工具

支持可配置的独立线程执行,通过将 call_in_task 设置为 true 即可在大模型调用此工具时,自动建立一条线程来执行相关的函数

用户可根据需要建立自定义工具,配合合适的提示词,实现基于大模型的控制需求

static mcp_tool_t mcp_tools[] = {
    { 
        .name = "self.get_device_status",
        .description = 
            "Provides the real-time information of the device, including the current status of the audio speaker, screen, battery, network, etc.\n"
            "Use this tool for: \n"
            "1. Answering questions about current condition (e.g. what is the current volume of the audio speaker?)\n"
            "2. As the first step to control the device (e.g. turn up / down the volume of the audio speaker, etc.)",
        .call = mcp_tool_handle_get_device_status,
        .call_in_task = false
    },
    {
        .name = "self.audio_speaker.set_volume",
        .description = 
            "Set the volume of the audio speaker. If the current volume is unknown, you must call `self.get_device_status` tool first and then call this tool.",
        .call = mcp_tool_handle_set_volume,
        .call_in_task = false,
        .properties = {
            {
                .name = "volume",
                .type = kPropertyTypeInteger,
                .has_min_value = true,
                .min_value = 0,
                .has_max_value = true,
                .max_value = 100
            }
        }
    },
    {
        .name = NULL
    }
};