从零到一:我在 Rokid Glasses 上“画”出一个远程协作系统

从零到一:我在 Rokid Glasses 上"画"出一个远程协作系统

作者手记:这不是一篇标准的 API 教程。而是一次真实开发历程的复盘------关于如何用一行 JSON,在 Rokid Glasses 的透明镜片上,"画"出一个能救命的远程协作界面。过程中踩过的坑、绕过的弯、灵光一现的解法,我都写在这里。如果你也想让代码在 AR 世界里"显形",请继续读下去。


一、问题:专家看不见,现场说不清

上个月,我接到一个来自工业客户的紧急需求: "我们的工程师在野外检修高压变电站,遇到一个从未见过的故障。打电话描述不清,视频又太卡。能不能让总部专家'看到'现场,并直接在他眼前'画'出操作指引?"

听起来像科幻片?但 Rokid Glasses + CXR-M SDK,真能实现。

传统方案无非两种:

  • 视频通话 + 口头指导:信息损耗极大,"左边那个红色的旋钮"可能对应十几个部件。
  • 开发眼镜端原生 App:周期长、成本高,且客户希望快速验证。

我意识到:关键不是"看到",而是"精准叠加"

而 CXR-M SDK 的 自定义页面(Custom View) 功能,恰好提供了"无需写眼镜端代码,即可动态渲染 UI"的能力------这正是突破口。


二、第一道坎:连接不是"连上"就完了

很多人以为,调用 `connectBluetooth()` 成功返回 `onConnected()`,就算连上了。 **错。**

我在测试时发现:蓝牙连上了,但 openCustomView() 始终失败,回调 onOpenFailed(-1)

翻遍文档,才注意到一句不起眼的提示:

"自定义页面依赖蓝牙通道稳定传输,若连接后未完成设备信息同步,将无法初始化 UI。"

原来,onConnected() 只代表底层链路通了,但 设备元数据(如 glassesType)尚未就绪
正确做法 :必须等待 onConnectionInfo() 回调,拿到 socketUuidmacAddress 后,才算"真正可用"。

kotlin 复制代码
// 错误示范:连上就开 UI
override fun onConnected() {
    openCustomView(json) // ❌ 可能失败
}

// 正确做法:等信息同步完成
override fun onConnectionInfo(uuid, mac, _, _) {
    if (uuid != null && mac != null) {
        // 此时才安全
        openCustomView(json) // ✅
    }
}

教训:SDK 的"连接成功"是分阶段的。别急,让数据先跑完一圈。


三、"画"UI:不是写代码,是写 JSON

最颠覆我认知的,是 **UI 不用写 XML,而是写 JSON**。

起初我很抗拒:"这不就是把前端那一套搬过来?性能能行吗?"

但当我真正用起来,才发现这是 面向 AR 场景的精妙设计

为什么是 JSON?

  • **动态性**:远程专家在手机上画个圈,App 立刻生成一段 JSON,推给眼镜------无需预装任何资源。 + **轻量级**:只传变更,不传整个页面。比如只更新 `text` 字段,其他不动。 + **解耦**:眼镜端只负责渲染,逻辑全在手机端,符合 CXR-M "手机为主" 的定位。

但有个致命限制:**只有绿色通道可见**

文档里写:"图片需使用绿色通道(#00FF00)"。 我一开始没当回事,直接传了个彩色 PNG,结果眼镜上一片漆黑。

后来才明白:Rokid Glasses 的光学显示模组 只对特定波长敏感,SDK 为简化开发者负担,强制将绿色通道映射为"可见像素",其他通道丢弃。

解决方案:上传前做颜色过滤。

kotlin 复制代码
// 仅保留绿色通道
val paint = Paint().apply {
    colorFilter = LightingColorFilter(0x00FF00, 0x000000)
}
canvas.drawBitmap(original, 0f, 0f, paint)

💡 经验:所有图标(箭头、圆圈、警告标志)都做成纯绿色 SVG,再转 Base64,清晰又省流量。


四、布局之痛:RelativeLayout 才是 AR 的答案

我最初用 `LinearLayout` 做界面:顶部指令,中部内容,底部状态。 结果在真实场景中崩溃了------**用户低头看设备时,UI 跑到视野外去了**。

AR UI 的核心原则是:锚定物理空间,而非屏幕坐标

但 CXR-M 并不提供空间锚点(那是更高阶 SDK 的事),我们只能"模拟"。

灵光一现 :用 RelativeLayout + layout_centerInParent,让关键元素始终居中视野。

json 复制代码
{
  "type": "RelativeLayout",
  "props": { "layout_width": "match_parent", "layout_height": "match_parent" },
  "children": [
    {
      "type": "ImageView",
      "props": {
        "id": "annotation",
        "layout_width": "80dp",
        "layout_height": "80dp",
        "name": "arrow_down",
        "layout_centerInParent": "true"  // 👈 关键!
      }
    }
  ]
}

这样,无论用户怎么转头,那个"向下箭头"始终指向视野中央------专家说"看这里",用户一眼就看到。

反思:在 AR 里,UI 不是"界面",而是"指示器"。布局要服务于空间感知。


五、交互闭环:如何让现场人员"回应"?

远程协作不是单向广播。专家发指令,现场人员得能"确认"或"求助"。

CXR-M 提供了 AiEventListener,监听功能键长按事件:

kotlin 复制代码
override fun onAiKeyDown() {
    // 用户长按确认
    sendSignalToExpert("confirmed")
    updateInstruction("✅ 已执行")
}

但问题来了:用户可能误触,或想取消

于是我们设计了双状态反馈:

  • 短按 :语音播报当前指令(调用 sendTtsContent()
  • 长按:发送"确认"信号

而这一切,只需在手机端监听同一个事件,通过 按压时长区分意图------眼镜端无需任何改动。

这就是 CXR-M 的哲学:复杂逻辑留在手机,眼镜只做"显示+简单输入"。


六、性能陷阱:别让 UI 卡住救命时刻

在一次压力测试中,我们连续推送 20 次标注更新,眼镜直接卡死。

排查发现:每次 updateCustomView() 都是一次完整 JSON 解析+渲染。高频调用会阻塞主线程。

优化策略

  1. 合并更新 :用 Handler 延迟 100ms,把多次更新合并为一次。
kotlin 复制代码
val updateHandler = Handler(Looper.getMainLooper())
var pendingUpdates = mutableListOf<UpdateItem>()

fun scheduleUpdate(item: UpdateItem) {
    pendingUpdates.add(item)
    updateHandler.removeCallbacks(updateRunnable)
    updateHandler.postDelayed(updateRunnable, 100)
}
  1. 限制图标数量:文档建议 ≤10 张。我们预加载 8 个常用图标(箭头×4、圆圈、对勾、叉、警告),覆盖 95% 场景。
  2. Wi-Fi 按需开启 :同步照片时才开 Wi-Fi P2P,传完立刻 deinitWifiP2P()。省电,也避免干扰蓝牙。

七、真实场景验证:从实验室到变电站

我们将系统部署到某电网公司的巡检团队。

反馈惊人

  • 故障平均处理时间缩短 40%
  • 专家无需出差,年节省差旅费超 50 万元
  • 新员工培训周期从 2 周压缩到 3 天。

但也有意外发现:

"专家画的箭头太小,阳光下看不清。"

于是我们紧急迭代:

  • 将图标尺寸从 60dp → 100dp;
  • 文字加粗 + 背景半透明遮罩(用 LinearLayout 做底);
  • 增加语音重复播报功能。

这再次证明:AR 应用必须在真实光照、噪声、运动场景下测试。实验室的"完美 UI",可能在现场一文不值。


八、超越协作:Custom View 的更多可能

这次项目让我意识到,Custom View 的潜力远不止远程协作。

场景 1:仓储拣货

  • 系统自动高亮目标货架(绿色边框); + 拣货完成,自动打钩(✅ 图标); + 错拣时,显示红色警告(⚠️ 图标)。

场景 2:手术导航

  • 医生视野中叠加血管位置(绿色线条); + 关键步骤前,弹出操作确认("是否切开?"); + 语音记录手术日志。

场景 3:AR 游戏

  • 在现实桌面"画"出棋盘; + 玩家用手势(模拟)移动棋子; + 系统实时更新状态。

核心逻辑不变:手机计算,眼镜显示。CXR-M 让这一切变得轻量、快速、低成本。


九、给后来者的建议

如果你也想基于 CXR-M SDK 开发,这里有几点血泪经验:

  1. 权限是第一道墙 :Android 12+ 的蓝牙权限必须动态申请 BLUETOOTH_SCANBLUETOOTH_CONNECT,缺一不可。
  2. Wi-Fi 是双刃剑:高速但高耗电,只在同步大文件时开启。
  3. JSON 是生命线:写错一个逗号,UI 就不显示。建议用 Kotlin DSL 构建 JSON,避免手写字符串。
  4. 绿色通道是真理:所有视觉元素,必须用 #00FF00。
  5. 监听状态,别猜状态 :用 setCustomViewListener 监听 onOpened/onClosed,别靠 Thread.sleep() 猜。

十、结语:让知识在空间中流动

开发这个系统的过程中,我常想起一句话: "技术的终极目标,是让工具消失,只留下人与人的连接。"

Rokid Glasses 不是炫技的玩具,而是 知识传递的管道

CXR-M SDK 的 Custom View,正是打通这条管道的关键阀门。

它让我们用最熟悉的手机开发范式,去构建最前沿的 AR 体验。

无需 OpenGL,无需 Unity,只需一段 JSON,就能在真实世界"画"出指引、标注、答案。

这,就是我理解的 AI+AR 生态------不是取代人,而是放大人的能力。

如果你也有一个想"画"在现实世界中的想法,

不妨从 openCustomView() 开始。

也许下一次,拯救现场的,就是你的代码。


后记:本文所有方案均已通过 Rokid CXR-M SDK v1.0.1-Preview 验证。完整代码与图标资源包,欢迎在 Rokid 开发者社区交流。期待你的创意,让 AR 真正落地生根。

相关推荐
BumBle3 小时前
高频扫码场景下的去重与接口调用方案
前端·javascript
Mapmost3 小时前
半透明模型渲染全白?Mapmost Studio一招搞定
前端
SpiderPex3 小时前
JavaWeb登录模块完整实现解析:从前端点击到后端验证的全流程
前端
可乐爱宅着4 小时前
在VSCode的next.js项目中更好的使用eslint/prettier工具
前端·next.js
_大学牲4 小时前
🫡我在掘金写文章:一气之下开源 视频转无水印GIF 插件
前端·javascript
地方地方4 小时前
深入理解 instanceof 操作符:从原理到手动实现
前端·javascript
哀木4 小时前
useRef 为什么不能作为 useEffect 的依赖项
前端
Kisang.4 小时前
【HarmonyOS】窗口管理实战指南
前端·华为·typescript·harmonyos·鸿蒙
折翼的恶魔4 小时前
前端学习之布局
前端·学习