ARCore 中文文档 下载 App

Cloud Anchors 云锚点跨设备共享 AR 体验

Cloud Anchors 解决什么问题

普通 ARCore Anchor 的生命周期是本地的——你在手机 A 上放了一个虚拟雕像,把手机交给另一个人,他看不到这个雕像。

Cloud Anchors 把本地锚点的特征点地图托管到 Google Cloud,返回一个唯一的字符串 ID。任何其他设备用这个 ID 就能下载特征点地图,在同一真实位置解析出同一个锚点——从而在不同手机上看到完全相同的 AR 内容。

Cloud Anchors 的工作流程

设备 A(托管方)          Google Cloud          设备 B(解析方)
──────────────────────────────────────────────────────────────
创建本地 Anchor
托管锚点
(上传:特征点地图 + 当前姿态)
                    → 存储地图,返回 anchorId
    ←──────────────────
收到 anchorId

                                    用 anchorId 请求解析
                    ← 发送 anchorId
(下载:特征点地图)
                    → 上传当前摄像头画面做匹配
(匹配成功,计算设备姿态)
                    ← 返回解析后的 Anchor
收到解析的 Anchor(和设备 A 的是同一个真实位置)

两阶段模型

阶段API说明
托管(Host)session.hostCloudAnchor(anchor)本地 Anchor 的特征点地图上传到云端,获得 anchorId
解析(Resolve)session.resolveCloudAnchor(anchorId)用 anchorId 从云端下载地图,在当前设备上创建匹配的 Anchor

托管端(Host)示例

// 1. 先在本地 SLAM 中创建好锚点
val localAnchor = session.createAnchor(hitResult.pose)

// 2. 托管到云端
val future = session.hostCloudAnchor(localAnchor, TTL_DAYS = 7)

// 3. 异步等待托管完成
future.addListener({
    if (future.result?.cloudAnchorState == CloudAnchorState.SUCCESS) {
        val anchorId = future.result!.anchorId  // 这是一个字符串
        // 把 anchorId 分享给其他设备(通过网络、二维码、直接传等)
        shareAnchorId(anchorId)
    }
}, Handler(Looper.getMainLooper()))

解析端(Resolve)示例

// 1. 拿到从设备 A 分享来的 anchorId
val anchorId = sharedAnchorId

// 2. 开始解析
val future = session.resolveCloudAnchor(anchorId)

// 3. 异步等待解析完成
future.addListener({
    val state = future.result?.cloudAnchorState
    when (state) {
        CloudAnchorState.SUCCESS -> {
            val cloudAnchor = future.result!!
            // cloudAnchor 和本地 Anchor 一样用法
            // cloudAnchor.pose 是在设备 B 的坐标系下对应的真实世界位置
            placeContentAtAnchor(cloudAnchor)
        }
        CloudAnchorState.TTL_EXPIRED -> {
            // 锚点已过期(超过设置的 TTL 天数)
        }
        CloudAnchorState.NOT_FOUND -> {
            // anchorId 无效或被删除
        }
    }
}, Handler(Looper.getMainLooper()))

TTL:锚点过期时间

Cloud Anchors 有两种过期设置:

TTL 模式托管参数说明
默认不指定 TTL锚点保留 24 小时
自定义hostCloudAnchor(anchor, ttlDays)最长 365 天
永久hostCloudAnchor(anchor, 0) 或使用永久模式长期保留(消耗更多配额)

TTL 到期后,锚点在云端被删除,用这个 ID 解析会返回 NOT_FOUND。

配额与定价

Cloud Anchors 有免费额度(详见定价授权页面):

监控用量

在 Google Cloud Console 中可以实时查看 Cloud Anchors 的用量和剩余配额。商业应用应该设置用量告警,避免超出配额导致功能中断。

与 Geospatial API 结合

Cloud Anchors + Geospatial API 是最强组合:

// 在服务器上预先托管锚点
// 或利用 Geospatial Anchor 的位置创建 Cloud Anchor
val geoAnchor = earth.createAnchor(lat, lng, altitude)
val cloudFuture = session.hostCloudAnchor(geoAnchor)
// 获得 anchorId 后,任何走到这个经纬度的用户都能解析

多人实时协作场景

Cloud Anchors 本身只负责静态锚点的创建和解析——它不提供实时同步。要实现多人实时共享同一 AR 场景,你需要自己写一个实时同步层(Firebase Realtime Database、WebSocket 等):

设备 A 托托管 Cloud Anchor → 获得 anchorId
                         → 把 anchorId 发给 Firebase
                         → 同时把设备 A 的虚拟物体位置推给 Firebase

设备 B 收到通知 → 用 anchorId Resolve Cloud Anchor
                → 订阅 Firebase 中的虚拟物体位置
                → 在 Cloud Anchor 坐标系中渲染来自设备 A 的虚拟物体

Google 的 Just a Line(多人 AR 绘画应用)和 Squanch Chat(多人 AR 社交)都是这种架构。

常见失败状态处理

CloudAnchorState含义处理
SUCCESS托管/解析成功正常使用
ERROR_INTERNAL云端内部错误可以重试,通常几秒后恢复
ERROR_NOT_AUTHORIZEDAPI Key 无效检查 Cloud Anchors API Key 配置
ERROR_RATE_LIMITED频率超限降低请求频率,或申请提高配额
ERROR_RESOURCE_EXHAUSTED配额用完升级计划或等下个月重置
TTL_EXPIRED锚点已过期让托管方重新托管
NOT_FOUNDanchorId 无效让托管方检查 ID 是否正确