You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在RoomPlan会话中通过ARKit Raycast将CoreML输出的2D视觉边界框精准映射到3D世界坐标?

如何在RoomPlan会话中通过ARKit Raycast将CoreML输出的2D视觉边界框精准映射到3D世界坐标?

这个问题我之前在做AR墙面缺陷检测的项目时也踩过不少坑——核心痛点就是ARKit的预估平面太粗糙,和RoomPlan重建的精准墙面不在一个精度层级,稍不注意还会出现坐标空间错位。下面分几个核心点给你拆解解决方案:

一、优先用RoomPlan的精准墙面替代ARKit Estimated Plane

RoomPlan的CapturedRoom.Surface是经过多帧优化后的精准重建结果,比ARKit的.estimatedPlane(仅基于少量点云的粗略估计)靠谱太多。具体做法是:从2D缺陷点生成ARKit射线,再计算射线与RoomPlan墙面的精确交点,而非依赖ARKit自动的raycast结果。

实现步骤:

  1. 从2D视觉坐标生成精准的ARKit射线
    不要直接用屏幕坐标转raycast query,而是基于AR相机的内参计算射线,避免屏幕缩放/旋转带来的误差:

    import ARKit
    import simd
    
    func createRayFromVisionBounds(boundingBox: VNRecognizedObjectObservation, 
                                  camera: ARCamera, 
                                  viewSize: CGSize) -> (origin: simd_float3, direction: simd_float3) {
        // 1. 把Vision归一化坐标转成相机帧的归一化坐标(Vision原点在左上角,ARKit在左下角,需翻转Y轴)
        let visionMidX = boundingBox.boundingBox.midX
        let visionMidY = 1 - boundingBox.boundingBox.midY
        let normalizedCameraPoint = CGPoint(x: visionMidX, y: visionMidY)
        
        // 2. 用AR相机内参计算射线方向
        let intrinsics = camera.intrinsics
        let fx = intrinsics[0][0]
        let fy = intrinsics[1][1]
        let cx = intrinsics[2][0]
        let cy = intrinsics[2][1]
        
        let x = (normalizedCameraPoint.x * viewSize.width - cx) / fx
        let y = (normalizedCameraPoint.y * viewSize.height - cy) / fy
        
        let rayDirection = simd_normalize(simd_float3(x, y, 1))
        // 3. 射线原点是相机的世界位置
        let rayOrigin = camera.transform.columns.3.xyz
        
        return (rayOrigin, rayDirection)
    }
    
  2. 计算射线与RoomPlan墙面的精确交点
    遍历RoomPlan实时更新的CapturedRoom中的所有垂直墙面,计算射线和墙面平面的交点,并验证交点是否在墙面实际范围内:

    func findDefectWorldPosition(ray: (origin: simd_float3, direction: simd_float3), 
                                currentRoom: CapturedRoom) -> simd_float3? {
        var validIntersections: [simd_float3] = []
        
        for surface in currentRoom.surfaces {
            // 只处理垂直墙面
            guard case .wall(let wall) = surface.kind, wall.alignment == .vertical else {
                continue
            }
            
            // 墙面的平面方程:ax + by + cz + d = 0
            let wallPlane = wall.plane
            let planeNormal = wallPlane.normal
            let planeDistance = wallPlane.distance
            
            // 计算射线与平面的交点
            let denominator = simd_dot(ray.direction, planeNormal)
            // 避免射线与平面平行
            guard abs(denominator) > 1e-6 else { continue }
            
            let t = -(simd_dot(ray.origin, planeNormal) + planeDistance) / denominator
            // 只保留相机前方的交点
            guard t > 0 else { continue }
            
            let intersection = ray.origin + ray.direction * t
            
            // 验证交点是否在墙面的边界范围内(过滤墙面外的无效交点)
            let wallBounds = wall.bounds
            guard intersection.x >= wallBounds.min.x && intersection.x <= wallBounds.max.x,
                  intersection.y >= wallBounds.min.y && intersection.y <= wallBounds.max.y,
                  intersection.z >= wallBounds.min.z && intersection.z <= wallBounds.max.z else {
                continue
            }
            
            validIntersections.append(intersection)
        }
        
        // 返回最近的有效交点(如果有多个墙面被射线穿过)
        return validIntersections.min {
            simd_distance($0, ray.origin) < simd_distance($1, ray.origin)
        }
    }
    

二、彻底解决坐标空间对齐问题

RoomPlan和ARKit的坐标空间是完全绑定的——RoomPlan的RoomCaptureSession本身就基于当前的ARSession运行,只要满足以下两个条件,就不会出现坐标漂移:

  1. 全程使用同一个ARSession:不要在捕获过程中重启ARSession,否则ARKit会重置世界原点,导致之前保存的缺陷坐标和最终重建的USDZ/DXF完全错位。
  2. 实时用最新的CapturedRoom计算:在RoomCaptureSessionDelegateroomCaptureSession(_:didUpdate:)回调中,用最新的CapturedRoom实例计算交点,因为RoomPlan会不断优化墙面的位置和形状,越到后期重建结果越精准。

关于USDZ/DXF导出的对齐:

  • RoomPlan导出的USDZ文件会直接使用捕获时的ARSession世界原点作为USDZ的世界原点,你计算得到的simd_float3坐标可以直接在USDZ中作为标记点的位置,无需额外转换。
  • 导出DXF时,DXF的2D平面基于RoomPlan的地面平面(默认Y=0,ARSession的Y轴垂直地面),你可以把3D坐标的X/Z作为DXF的平面坐标,Y作为缺陷的高度标记。

三、进一步提升精度的小技巧

  1. 多交点取平均:不要只用bounding box的中点,而是对bounding box的四个角都生成射线,计算所有有效交点的平均值,能大幅减少单一点的误差。
  2. 过滤低质量帧:如果ARCamera的trackingState不是.normal,或者帧的曝光度过高/过低,暂时不进行缺陷检测,避免无效的坐标计算。
  3. 校准帧方向:确保传入Vision的CVPixelBuffer方向和ARSession的相机帧方向完全一致,否则Vision返回的归一化坐标会出现偏移,导致射线方向错误。

备注:内容来源于stack exchange,提问作者張晏滕

火山引擎 最新活动