从零到一:我在 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 后,才算“真正可用”。

// 错误示范:连上就开 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 为简化开发者负担,强制将绿色通道映射为“可见像素”,其他通道丢弃。

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

// 仅保留绿色通道
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,让关键元素始终居中视野。

{
  "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,监听功能键长按事件:

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

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

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

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

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

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


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

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

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

优化策略

  1. 合并更新:用 Handler 延迟 100ms,把多次更新合并为一次。
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 真正落地生根。

posted @ 2025-10-13 15:48  fruge365  阅读(173)  评论(0)    收藏  举报