技术拆解:Rokid CXR-M SDK 构建 AI 智能提词眼镜助手连接到场景落地

一、开发背景

作为一名长期活跃在 Rokid 开发者社区的技术爱好者,我一直在思考:如何将 Rokid Glasses 的 AR 能力真正融入到高频、刚需的工作场景中?

提词器,就是这样一个典型场景。无论是线上直播、线下演讲,还是课堂教学,用户往往需要一边注视前方观众,一边记住冗长的台词。传统提词器要么依赖支架(破坏沉浸感),要么依赖手机/平板(频繁低头)。而 Rokid Glasses 凭借其轻量化、第一视角显示的特性,天然适合作为「隐形提词器」。

但市面上的提词方案大多局限于单端操作,缺乏与手机的协同能力。于是,我决定基于 Rokid CXR-M SDK,开发一款 手机端编辑 + 眼镜端显示 + AI 语音联动 的完整提词助手应用。

本文将完整记录从 SDK 集成、设备连接、提词场景构建,到 AI 联动、异常处理、性能优化的全过程,希望能为其他开发者提供一份可复用的实操指南。


二、技术选型与整体架构

2.1 核心技术栈

  • 移动端:Kotlin + Android Jetpack(ViewModel, LiveData)
  • SDK:Rokid CXR-M SDK v1.0.1-Preview(仅支持 Android)
  • 通信协议:蓝牙(控制信令) + Wi-Fi P2P(媒体同步)
  • UI 渲染:眼镜端使用 SDK 内置的「自定义页面场景」(JSON 驱动)

2.2 系统架构图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

整个系统以 手机为控制中枢,眼镜为 显示与交互终端。CXR-M SDK 作为桥梁,封装了底层通信细节,让我们能专注于业务逻辑。


三、项目初始化:集成 CXR-M SDK

3.1 配置 Maven 仓库

settings.gradle.kts 中添加 Rokid 私有仓库:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        maven { url = uri("https://maven.rokid.com/repository/maven-public/") }
        google()
        mavenCentral()
    }
}

3.2 添加依赖与权限

build.gradle.kts 中:

android {
    defaultConfig {
        minSdk = 28 // 必须 ≥28
    }
}

dependencies {
    implementation("com.rokid.cxr:client-m:1.0.1-20250812.080117-2")
    // 其他 transitive 依赖(如 Retrofit, OkHttp)SDK 已声明,无需重复添加
}

AndroidManifest.xml 中声明权限(缺一不可):

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

⚠️ 注意:从 Android 12(API 31)起,蓝牙扫描/连接需动态申请 BLUETOOTH_SCANBLUETOOTH_CONNECT,且必须搭配 ACCESS_FINE_LOCATION

3.3 权限动态申请封装

我封装了一个 PermissionHelper,在 MainActivity 启动时调用:

class PermissionHelper(private val activity: AppCompatActivity) {
    fun requestRequiredPermissions(onGranted: () -> Unit) {
        val permissions = mutableListOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.BLUETOOTH,
            Manifest.permission.BLUETOOTH_ADMIN
        )
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            permissions += Manifest.permission.BLUETOOTH_SCAN
            permissions += Manifest.permission.BLUETOOTH_CONNECT
        }

        if (permissions.all { activity.checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED }) {
            onGranted()
        } else {
            activity.requestPermissions(permissions.toTypedArray(), REQUEST_CODE)
        }
    }
}

只有权限全部授予,才进入设备连接流程。


四、设备连接:蓝牙 + Wi-Fi 双通道建立

4.1 蓝牙连接流程

CXR-M SDK 的蓝牙连接分为两步:初始化连接

// 1. 初始化蓝牙(传入扫描到的 BluetoothDevice)
CxrApi.getInstance().initBluetooth(context, device, object : BluetoothStatusCallback {
    override fun onConnectionInfo(socketUuid, macAddress, rokidAccount, glassesType) {
        // 保存 socketUuid 和 macAddress,用于后续连接
        connectBluetooth(socketUuid!!, macAddress!!)
    }
    override fun onConnected() { /* 连接成功 */ }
    override fun onDisconnected() { /* 断开处理 */ }
    override fun onFailed(errorCode) { /* 错误处理 */ }
})

// 2. 正式连接
private fun connectBluetooth(uuid: String, mac: String) {
    CxrApi.getInstance().connectBluetooth(context, uuid, mac, callback)
}

💡 提示:socketUuid 是 Rokid Glasses 在 BLE 广播中携带的自定义服务 UUID(00009100-...),用于建立 RFCOMM 通道。

4.2 Wi-Fi P2P 初始化(用于后续文件同步)

提词内容虽小,但若未来扩展图片/视频提词,则需 Wi-Fi 通道。初始化如下:

val wifiStatus = CxrApi.getInstance().initWifiP2P(object : WifiP2PStatusCallback {
    override fun onConnected() {
        Log.d("WiFi", "P2P connected")
    }
    override fun onDisconnected() { /* ... */ }
    override fun onFailed(errorCode) { /* ... */ }
})

⚠️ 注意:Wi-Fi P2P 必须在蓝牙连接成功后才能初始化,且耗电较高,建议按需开启。


五、核心功能实现:提词器场景构建

5.1 打开提词器场景

fun openWordTips() {
    CxrApi.getInstance().controlScene(
        ValueUtil.CxrSceneType.WORD_TIPS,
        true, // true = 打开
        null
    )
}

5.2 发送提词内容

提词内容以 ByteArray 形式通过 sendStream 发送:

fun sendScript(text: String, fileName: String = "script.txt") {
    CxrApi.getInstance().sendStream(
        ValueUtil.CxrStreamType.WORD_TIPS,
        text.toByteArray(),
        fileName,
        object : SendStatusCallback {
            override fun onSendSucceed() {
                Log.d("Script", "Sent successfully")
            }
            override fun onSendFailed(errorCode) {
                Log.e("Script", "Send failed: $errorCode")
            }
        }
    )
}

5.3 配置提词器显示参数

支持字体大小、行距、显示区域、滚动模式等:

fun configureWordTips() {
    CxrApi.getInstance().configWordTipsText(
        textSize = 18f,      // 字体大小(sp)
        lineSpace = 1.5f,    // 行距
        mode = "ai",         // "ai" 模式支持 ASR 自动滚动
        startPointX = 100,   // X 起始坐标(像素)
        startPointY = 200,   // Y 起始坐标
        width = 800,         // 显示区域宽
        height = 600         // 显示区域高
    )
}

5.4 AI 模式下的 ASR 联动

当用户说话时,手机端 ASR 引擎识别结果,实时发送给眼镜,触发自动滚动:

fun onAsrResult(text: String) {
    CxrApi.getInstance().sendAsrContent(text) // 注意:此处复用 AI 场景的 ASR 接口
}

📌 关键点:提词器的 "ai" 模式会监听 ASR 内容,当识别文本接近当前显示末尾时,自动向上滚动,确保用户始终看到下一句。


六、增强体验:设备状态控制与监听

6.1 远程调节亮度/音量

演讲环境光线多变,允许用户在手机端滑动调节眼镜亮度:

// 设置监听器(获取当前亮度)
CxrApi.getInstance().setBrightnessUpdateListener { brightness ->
    viewModel.updateBrightnessUI(brightness)
}

// 设置亮度(0-15)
CxrApi.getInstance().setGlassBrightness(10)

同理,音量控制也支持双向同步。

6.2 电量与充电状态监听

CxrApi.getInstance().setBatteryLevelUpdateListener { level, charging ->
    Log.d("Battery", "Level: $level%, Charging: $charging")
    if (level < 20 && !charging) {
        showLowBatteryWarning()
    }
}

6.3 自动关机与熄屏策略

为节省电量,设置 10 分钟无操作自动关机:

CxrApi.getInstance().setPowerOffTimeout(10) // 单位:分钟
CxrApi.getInstance().setScreenOffTimeout(60) // 60秒无操作熄屏

七、异常处理与健壮性设计

7.1 连接断开重连机制

监听 onDisconnected(),尝试自动重连:

override fun onDisconnected() {
    viewModel.setConnectionStatus(false)
    // 延迟 3 秒后重试
    handler.postDelayed({
        if (lastMac != null && lastUuid != null) {
            connectBluetooth(lastUuid!!, lastMac!!)
        }
    }, 3000)
}

7.2 权限缺失友好提示

若用户拒绝关键权限,引导其手动开启:

if (!allGranted) {
    AlertDialog.Builder(this)
        .setTitle("权限不足")
        .setMessage("请授予位置和蓝牙权限,否则无法连接眼镜")
        .setPositiveButton("去设置") { _, _ ->
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            intent.data = Uri.parse("package:$packageName")
            startActivity(intent)
        }
        .show()
}

7.3 SDK 错误码解析

CXR-M SDK 定义了详细的错误码(如 CxrBluetoothErrorCode.SOCKET_CONNECT_FAILED),我在日志中统一映射为用户可读信息:

when (errorCode) {
    ValueUtil.CxrBluetoothErrorCode.BLE_CONNECT_FAILED -> "蓝牙连接失败,请靠近眼镜重试"
    ValueUtil.CxrWifiErrorCode.WIFI_DISABLED -> "请先打开手机 Wi-Fi"
    else -> "未知错误,请重启应用"
}

八、性能优化与用户体验细节

8.1 避免频繁发送相同内容

在发送提词脚本前,做内容哈希比对,避免重复传输:

if (currentScriptHash != newScript.hashCode()) {
    sendScript(newScript)
    currentScriptHash = newScript.hashCode()
}

8.2 Wi-Fi P2P 按需开启

仅在用户点击“同步历史图片”时才初始化 Wi-Fi,用完立即 deinitWifiP2P()

8.3 自定义页面作为备选方案

若未来提词需求复杂(如图文混排),可使用「自定义页面场景」:

{
  "type": "LinearLayout",
  "props": { "orientation": "vertical" },
  "children": [
    { "type": "TextView", "props": { "text": "标题", "textSize": "20sp" } },
    { "type": "ImageView", "props": { "name": "icon1" } }
  ]
}

先调用 sendCustomViewIcons() 上传图标,再 openCustomView(json) 显示。


九、总结与展望

通过本次实践,我深刻体会到 Rokid CXR-M SDK 的强大与易用

  • 连接稳定:蓝牙 + Wi-Fi 双通道设计,兼顾控制与传输;
  • 场景丰富:提词器、翻译、AI 助手等开箱即用;
  • 控制精细:从亮度、音量到关机策略,全面掌控设备;
  • 扩展灵活:自定义页面 JSON 方案,极大降低眼镜端开发门槛。

目前,这款「AI 智能提词眼镜助手」已在内部测试中获得良好反馈。下一步,我计划:

  1. 集成云端脚本管理,支持多设备同步;
  2. 增加多语言实时翻译提词;
  3. 利用摄像头实现「眼神控制滚动」。

Rokid Glasses 不只是一副眼镜,更是通往空间计算未来的入口。而 CXR-M SDK,正是我们开发者手中的钥匙。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐