基于Rokid Glasses的AI助盲应用实践:让科技点亮视障者的世界

一、项目背景与意义
在人工智能技术快速发展的今天,AI智能眼镜作为AI与现实世界交互的重要载体,正在改变我们的生活方式。对于视障人群而言,如何通过技术手段帮助他们更好地感知和理解周围环境,是一个具有重要社会意义的课题。华为手机推出的"小艺看世界"功能,通过AI图像识别帮助用户了解周围环境,这给了我们很大的启发。然而,小艺看世界是在手机上实现的,对于视障用户来说存在明显的不友好之处,需要拿出手机、打开应用、对准目标、按下拍照按钮,整个过程需要双手操作,对于视障用户来说非常困难。
Rokid Glasses作为AI智能眼镜,天然适合视障辅助应用,眼镜佩戴在头上,无需手持设备,可以在任何场景下使用,而且摄像头位置与眼睛位置一致,拍摄角度更自然,识别更准确。基于这个思路,使用Rokid Glasses 和Rokid CXR-M SDK,开发了一款AI助盲应用"盲导",通过语音指令触发拍照、AI图像识别和TTS语音播报,帮助视障用户识别周围环境、寻找物品、认路导航等,让科技真正服务于特殊人群的需求。
二、技术架构设计
2.1 整体架构
本项目采用手机端+眼镜端协同工作的架构模式:
语音识别] C2[意图识别服务] C3[场景解析服务
AI图像分析] end A1 -->|AI按键事件| B4 B4 -->|onAiKeyDown| B5 B5 -->|开始录音| A2 A2 -->|音频流数据| B5 B4 -->|onAiKeyUp| B5 B5 -->|停止录音| A2 B5 -->|录音数据| B6 B6 -->|发送音频| C1 C1 -->|识别文本| B6 B6 -->|文本| C2 C2 -->|意图类型| B6 B6 -->|意图判断| B8 B8 -->|拍照指令| A3 A3 -->|照片数据| B7 B7 -->|上传照片| C3 C3 -->|场景描述| B7 B7 -->|描述文本| B8 B8 -->|TTS文本| A4 B2 <-->|蓝牙双向通信| A1 B2 <-->|蓝牙双向通信| A2 B2 <-->|蓝牙双向通信| A3 B3 -->|设备绑定信息| B2 style Glasses fill:#e1f5ff style Phone fill:#fff4e1 style Cloud fill:#ffe1f5
2.2 核心模块
手机端提供了Android SDK,基于Android APP的开发方式开发手机端,作为Glasses端与云端AI的中介。
2.2.1 工程整体结构
项目核心类代码如下:
bash
app/src/main/java/com/qingkouwei/rokidclient2/
├── MainActivity.kt # 主Activity,应用入口
├── MainViewModel.kt # 主业务逻辑ViewModel
├── DeviceBindingActivity.kt # 设备绑定页面
├── DeviceBindingViewModel.kt # 设备绑定逻辑
│
├── BluetoothHelper.kt # 蓝牙扫描和发现
├── RokidConnectionManager.kt # Rokid SDK连接封装
├── DeviceBindingManager.kt # 设备绑定信息持久化
│
├── AIEventListenerManager.kt # AI事件监听管理
├── AudioRecordManager.kt # 录音管理
├── ASRIntentService.kt # ASR和意图识别
├── SceneAnalysisService.kt # 场景解析服务
├── TTSManager.kt # TTS语音播报管理
│
├── PhotoCaptureManager.kt # 拍照功能封装
├── AIImageAnalyzer.kt # AI图像分析(可选)
└── RokidSDKStub.kt # SDK接口桩(开发环境)
2.2.2 核心模块介绍
2.2.2.1 设备绑定模块
DeviceBindingManager.kt - 设备绑定信息管理
- 使用SharedPreferences持久化保存设备绑定信息
- 管理设备名称、地址、socketUuid、macAddress
- 提供设备绑定状态检查接口
DeviceBindingActivity.kt - 设备绑定UI
- 扫描周边Rokid设备
- 显示设备列表供用户选择
- 处理设备绑定和连接流程
BluetoothHelper.kt - 蓝牙扫描管理
- 使用UUID
0000be80-0000-1000-8000-00805f9b34fb过滤Rokid设备 - 支持已配对设备和扫描发现设备
- 完整的权限管理和蓝牙状态监听
2.2.2.2 连接管理模块
RokidConnectionManager.kt - Rokid SDK连接封装
- 封装
initBluetooth()和connectBluetooth()调用 - 统一连接回调处理
- 支持自动重连机制
2.2.2.3 AI交互模块
AIEventListenerManager.kt - AI事件监听
- 设置和取消AI事件监听器
- 处理
onAiKeyDown、onAiKeyUp、onAiExit事件
AudioRecordManager.kt - 录音管理
- 管理
openAudioRecord()和closeAudioRecord()调用 - 通过
AudioStreamListener收集音频流数据 - 返回完整的录音数据
2.2.2.4 业务处理模块
ASRIntentService.kt - ASR和意图识别
- 处理语音识别(调用云端ASR能力)
- 识别用户意图(拍照、导航等)
- 返回识别文本和意图类型
SceneAnalysisService.kt - 场景解析
- 分析照片场景(调用云端大模型解析图片内容)
- 生成适合视障用户理解的描述文本
TTSManager.kt - TTS语音播报
- 使用
sendTTSContent()发送文本到眼镜端 - 处理TTS播放状态回调
- 管理播放完成和错误处理
2.2.2.5 业务逻辑控制模块
MainViewModel.kt - 主业务逻辑
- 协调各模块工作
- 处理设备自动连接
- 实现完整的AI交互流程:录音 → ASR → 意图识别 → 拍照 → 场景解析 → TTS播报
2.3 完整工作流程
步骤1:应用启动和设备绑定
kotlin
// MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bindingManager = DeviceBindingManager(this)
// 检查设备是否已绑定
if (!bindingManager.isDeviceBound()) {
// 未绑定,跳转到绑定页面
val intent = Intent(this, DeviceBindingActivity::class.java)
startActivity(intent)
finish()
return
}
// 已绑定,自动连接
viewModel.autoConnect(bindingManager, bluetoothHelper)
}
}
绑定流程:
- 首次安装时,检查设备绑定状态
- 未绑定则跳转到设备绑定页面
- 扫描周边Rokid设备(使用UUID过滤)
- 用户选择设备并绑定
- 连接成功后保存设备信息(名称、地址、socketUuid、macAddress)
- 跳转到主页面
自动连接流程:
- 应用启动时检查是否已绑定设备
- 从SharedPreferences读取绑定信息
- 查找已配对设备或使用保存的连接信息
- 调用
connectBluetooth()建立连接 - 连接成功后设置AI事件监听
步骤2:设置AI事件监听
kotlin
// MainViewModel.kt
private fun setupAIEventListener() {
aiEventListenerManager.setAIEventListener(
object : AIEventListenerManager.AIEventListenerCallback {
override fun onAiKeyDown() {
// AI按键按下,开始录音
startRecording()
}
override fun onAiKeyUp() {
// AI按键释放,停止录音并处理
stopRecordingAndProcess()
}
override fun onAiExit() {
// AI场景退出
}
},
set = true
)
}
步骤3:录音处理
kotlin
// MainViewModel.kt
private fun startRecording() {
audioRecordManager.startRecording(
object : AudioRecordManager.AudioRecordCallback {
override fun onRecordingStarted() {
updateStatus("正在录音...")
}
}
)
}
private fun stopRecordingAndProcess() {
viewModelScope.launch {
val audioData = audioRecordManager.stopRecording()
if (audioData != null && audioData.isNotEmpty()) {
processRecordedAudio(audioData)
}
}
}
//AudioRecordManager.kt
fun startRecording(callback: AudioRecordCallback) {
this.callback = callback
// Set audio stream listener
CxrApi.getInstance().setAudioStreamListener(audioStreamListener)
// Open audio record
val status = CxrApi.getInstance().openAudioRecord(CODEC_TYPE_OPUS, STREAM_TYPE)
if (status == ValueUtil.CxrStatus.REQUEST_SUCCEED) {
Log.d(TAG, "Audio recording started")
} else {
Log.e(TAG, "Failed to start audio recording: $status")
callback.onError("Failed to start recording: $status")
}
}
/**
* Stop recording */suspend fun stopRecording(): ByteArray? = withContext(Dispatchers.IO) {
if (!isRecording) {
Log.w(TAG, "Not recording, cannot stop")
// Remove audio stream listener
CxrApi.getInstance().setAudioStreamListener(null)
return@withContext null
}
// Close audio record
val status = CxrApi.getInstance().closeAudioRecord(STREAM_TYPE)
// Remove audio stream listener
CxrApi.getInstance().setAudioStreamListener(null)
if (status == ValueUtil.CxrStatus.REQUEST_SUCCEED) {
isRecording = false
// Get recorded audio data
val audioData = audioDataBuffer.toByteArray()
recordedAudioData = audioData
audioDataBuffer.reset()
Log.d(TAG, "Audio recording stopped, data size: ${audioData.size}")
callback?.onRecordingStopped(audioData)
return@withContext audioData
} else {
Log.e(TAG, "Failed to stop audio recording: $status")
callback?.onError("Failed to stop recording: $status")
return@withContext null
}
}
录音流程:
onAiKeyDown事件触发,调用openAudioRecord()- 设置
AudioStreamListener接收音频流数据 - 持续收集音频数据到Buffer
onAiKeyUp事件触发,调用closeAudioRecord()- 返回完整的录音数据
步骤4:ASR和意图识别
kotlin
// MainViewModel.kt
private fun processRecordedAudio(audioData: ByteArray) {
viewModelScope.launch {
asrIntentService.processAudio(
audioData,
object : ASRIntentService.ASRCallback {
override fun onSuccess(result: ASRIntentService.ASRResult) {
// 发送ASR结果到眼镜端显示
CxrApi.getInstance().sendAsrContent(result.text)
CxrApi.getInstance().notifyAsrEnd()
// 根据意图执行相应操作
when (result.intent) {
ASRIntentService.IntentType.PHOTO -> {
handlePhotoIntent()
}
ASRIntentService.IntentType.UNKNOWN -> {
// 未识别意图
CxrApi.getInstance().notifyAsrError()
}
}
}
}
)
}
}
ASR和意图识别流程:
- 将录音数据发送到ASR服务(当前为模拟实现)
- 获取识别文本
- 将文本发送到意图识别服务
- 获取意图类型(拍照、导航等)
- 将识别文本发送到眼镜端显示
- 根据意图执行相应操作
步骤5:拍照和场景解析
kotlin
// MainViewModel.kt
private fun handlePhotoIntent() {
viewModelScope.launch {
// 打开相机
CxrApi.getInstance().openGlassCamera(1920, 1080, 80)
// 拍照
CxrApi.getInstance().takeGlassPhoto(
1920, 1080, 80,
object : PhotoResultCallback {
override fun onPhotoResult(status: ValueUtil.CxrStatus?, photo: ByteArray?) {
if (status == ValueUtil.CxrStatus.RESPONSE_SUCCEED && photo != null) {
// 分析场景
analyzeSceneAndSpeak(photo)
}
}
}
)
}
}
拍照流程:
- 调用
openGlassCamera()打开眼镜端相机 - 调用
takeGlassPhoto()触发拍照 - 照片以WebP格式通过蓝牙传输
PhotoResultCallback.onPhotoResult()回调接收照片数据
步骤6:场景解析和TTS播报
kotlin
// MainViewModel.kt
private fun analyzeSceneAndSpeak(photoData: ByteArray) {
viewModelScope.launch {
sceneAnalysisService.analyzeScene(
photoData,
object : SceneAnalysisService.AnalysisCallback {
override fun onSuccess(description: String) {
// 发送TTS内容到眼镜端播放
CxrApi.getInstance().sendTtsContent(
description,
object : TTSStatusCallback {
override fun onTTSStart() {
// TTS开始播放
}
override fun onTTSEnd() {
// TTS播放完成
}
override fun onTTSError(errorCode: Int) {
// TTS播放失败
}
}
)
}
}
)
}
}
场景解析和TTS流程:
- 将照片数据发送到场景解析服务(当前为模拟实现)
- AI模型分析场景,生成描述文本
- 调用
sendTtsContent()发送文本到眼镜端 - 眼镜端TTS引擎合成语音并播放
- 用户听到场景描述
2.4 完整流程时序图
三、实际应用场景演示
3.1 场景一:识别道路环境
用户需求:盲人需要了解前方道路情况,判断是否可以安全行走
工作流程:
- 用户说:"帮我看看前面的路"
- 应用触发拍照,获取前方道路图像
- AI分析:"前方是一条宽约2米的人行道,路面平整,右侧有盲道,左侧有绿化带,前方约10米处有一个垃圾桶,建议靠右行走"
- 通过TTS播放给用户
技术要点:
- 使用Rokid SDK的
openGlassCamera()和takeGlassPhoto()获取道路图像 - AI提示词重点强调道路宽度、障碍物、安全提示等信息
- 描述语言简洁明确,便于盲人理解
3.2 场景二:寻找丢失物品
用户需求:在房间内寻找丢失的钥匙
工作流程:
- 用户说:"帮我找找钥匙"
- 应用拍照识别房间环境
- AI分析:"在视野中,我看到一个茶几,茶几上有一串银色的钥匙,位置在茶几中央偏左,距离你约3米"
- 用户根据描述找到钥匙
技术要点:
- 多轮对话:先整体描述,再聚焦特定物品
- 位置描述使用相对位置(前后左右)和距离
- 物品特征描述(颜色、大小、形状)帮助识别
3.3 场景三:阅读文字信息
用户需求:识别门牌号或路牌
工作流程:
- 用户说:"帮我看看这个门牌号"
- 应用拍照识别
- AI分析:"门牌上写着:北京市朝阳区某某路123号"
- 清晰朗读给用户
技术要点:
- 使用GPT-4o Vision的强大文字识别能力
- 按顺序朗读,避免信息混乱
- 支持中英文混合识别
四、开发心得与总结
4.1 Rokid SDK使用体验
通过本项目的开发实践,我们深刻体验了Rokid CXR-M SDK的强大能力:
1. API设计清晰直观
kotlin
// 连接流程清晰,回调机制完善
CxrApi.getInstance().initBluetooth(context, device, callback)
CxrApi.getInstance().connectBluetooth(context, uuid, address, callback)
2. 功能覆盖全面
- 蓝牙连接:完整的BLE+经典蓝牙双通道支持
- 拍照功能:AI场景拍照,照片实时传输
- 设备控制:音量、亮度、电量等全方位控制
- 状态监听:连接状态、设备状态实时回调
- 覆盖了主要使用场景
3. 开发体验优秀
- SDK集成简单,只需添加依赖
- API调用直观,易于理解和使用
- 错误处理完善,便于调试
4.2 技术挑战与解决方案
挑战1:Kotlin版本兼容性
- 问题:Rokid SDK使用Kotlin 2.1.0编译,需要Java 17环境
- 解决:升级开发环境到Java 17,或使用兼容的Kotlin版本
挑战2:蓝牙连接稳定性
- 问题:蓝牙连接可能中断,需要保证稳定性
- 解决:实现完善的错误处理和自动重连机制
- 代码示例:
kotlin
override fun onDisconnected() {
// 连接断开,自动尝试重连
if (socketUuid != null && macAddress != null) {
connect(context, socketUuid!!, macAddress!!)
}
}
挑战3:AI响应时间
- 问题:AI API调用可能有延迟
- 解决:使用协程异步处理,显示处理进度,支持本地模型作为备选方案,在处理时进行友好语音提示,让用户感知到进度。
4.3 Rokid SDK能力总结
通过本项目的实践,我们充分验证了Rokid CXR-M SDK在以下方面的能力:
| 能力类别 | SDK功能 | 应用场景 |
|---|---|---|
| 连接能力 | 蓝牙双通道连接 | 手机与眼镜的稳定通信 |
| 数据交互 | AI场景拍照 | 获取眼镜端图像数据 |
| 设备控制 | 音量/亮度/电量 | 优化用户体验 |
| 状态管理 | 连接状态监听 | 实时反馈设备状态 |
| 扩展性 | 丰富的回调接口 | 支持复杂业务逻辑 |
4.4 未来展望
随着AI技术的不断发展和Rokid SDK能力的持续增强,AI助盲应用将能够:
- 更精准的环境理解:结合多帧图像进行3D场景重建,读取设备经纬度,识别更细微的环境变化,提供更准确的位置信息
- 更自然的交互体验:支持连续对话,理解上下文,个性化学习用户习惯,更智能的提示和建议
- 更多实用功能场景:实时导航引导物体跟踪和定位,社交场景识别(识别熟人、表情等)
- 更好的性能表现:支持离线AI模型,提升响应速度,降低功耗
五、总结
盲导项目展示了如何利用Rokid Glasses和Rokid CXR-M SDK,结合AI技术开发具有实际社会价值的应用。通过完整的蓝牙连接、拍照传输、AI识别和语音播报流程,为视障人群提供了一个实用的辅助工具。
Rokid SDK的强大能力为开发者提供了坚实的基础,使得我们可以专注于业务逻辑和用户体验的优化。无论是开发纯眼镜端应用,还是手机端配合眼镜的协同应用,Rokid SDK都能提供完整的支持。
相信随着技术的不断进步和Rokid SDK能力的持续增强,AI+AR的应用将在更多领域发挥重要作用,真正让科技服务于每一个人,让智能眼镜成为连接数字世界与现实世界的桥梁。
开发环境介绍 项目名称 :盲导 - AI助盲应用
技术栈 :Kotlin + Jetpack Compose + Rokid CXR-M SDK + OpenAI Vision API
开发平台 :Android
Rokid SDK版本 :1.0.1-20250812.080117-2
致谢:感谢Rokid提供的优秀SDK和开发支持,让AI+AR应用的开发变得更加简单高效。