Measure health with motion
Health & Fitness 进阶 15m

用运动测量健康

Measure health with motion

2021年6月9日

在 Apple 官方观看视频

一句话判断

Apple Watch 不仅是计步器——CMDeviceMotion 和 CMPedometer 组合起来,你的 App 可以做到从步态分析到跌倒检测的一切运动健康功能。

这场 Session 讲了什么

这个 Session 深入讲解了如何利用 Apple Watch 和 iPhone 的运动传感器数据来测量健康指标。苹果的运动框架栈分为几层:底层的 Core Motion(CMMotionManager)提供原始的加速度计和陀螺仪数据;中间层的 CMDeviceMotion 融合了传感器数据并校正了偏差;上层的 CMPedometerCMAltimeter 提供了高级的步数和楼层攀升数据。

Session 重点展示了两个场景:步态分析(Gait Analysis)和运动类型识别。步态分析通过 CMDeviceMotionuserAccelerationattitude 数据,结合信号处理算法,可以提取步长、步频、对称性等指标。这些数据在康复医学和老年人健康监测中有直接应用。运动类型识别则通过 CMMotionActivityManager 判断用户是在走路、跑步、骑车还是开车——虽然系统已经提供了这个能力,但 Session 讲了如何通过机器学习模型提高识别精度。

另外,Session 提到了 watchOS 8 中新增的运动健康 API:HealthKitHKWorkoutBuilder 支持实时数据流,你可以在运动进行中持续获取心率、步频等数据并即时展示。

值得深挖的点

从原始加速度数据提取步态特征。 CMDeviceMotion.userAcceleration 给你的是设备坐标系下的三轴加速度(已去除重力分量)。要从中提取步态信息,你需要先做坐标变换——把设备坐标系的加速度转换到世界坐标系(使用 CMDeviceMotion.attitude 的旋转矩阵),然后对垂直方向的加速度做峰值检测。相邻两个峰值之间的时间间隔就是一步的时间,倒数就是步频。水平方向的位移可以通过对加速度做双重积分来估算(虽然积分漂移是个大问题,但短时间内的估算还是可用的)。Session 强调:不要从头写这些信号处理算法,苹果的 CMPedometer 已经内置了比你自己实现更精确的步数检测。

HKWorkoutBuilder 的实时数据流。 watchOS 8 的 HKWorkoutBuilder 支持通过 beginCollection(withStartDate:)endCollection(withEndDate:) 来定义一次运动的时间窗口。在这期间,你可以通过 dataSource 订阅特定类型的数据更新(心率、距离、卡路里等),每次有新数据时自动触发回调。这比以前轮询 HealthKit 的方式高效得多,也是实现实时运动仪表盘的基础。

代码片段

import CoreMotion

// 使用 CMPedometer 获取步数数据
let pedometer = CMPedometer()

func startStepCounting() {
    guard CMPedometer.isStepCountingAvailable() else { return }

    // 实时步数更新
    pedometer.startUpdates(from: Date()) { data, error in
        guard let data = data else { return }
        let steps = data.numberOfSteps
        let distance = data.distance  // 可选,估计距离
        let pace = data.currentPace   // 可选,当前步速
        let cadence = data.currentCadence  // 可选,当前步频

        print("步数: \(steps), 距离: \(distance?.intValue ?? 0) 米")
    }
}
import HealthKit

// 使用 HKWorkoutBuilder 构建实时运动数据流
let healthStore = HKHealthStore()
let workoutBuilder = HKWorkoutBuilder(
    healthStore: healthStore,
    configuration: HKWorkoutConfiguration(),
    device: nil
)

func startWorkout() async throws {
    try await workoutBuilder.beginCollection(at: Date())

    // 订阅心率数据更新
    let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
    let dataSource = HKLiveWorkoutDataSource(
        healthStore: healthStore,
        workoutConfiguration: HKWorkoutConfiguration()
    )
    dataSource.enableAutoHeartRateDetection(true)

    // 实时处理数据
    workoutBuilder.delegate = self
}

// HKWorkoutBuilderDelegate
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKWorkoutBuilder) {
    // 新的运动数据已收集
    let heartRate = workoutBuilder.latestStatistics(
        for: HKQuantityType.quantityType(forIdentifier: .heartRate)!
    )
}
// 检测运动类型变化
let activityManager = CMMotionActivityManager()

func startActivityMonitoring() {
    activityManager.startActivityUpdates(to: .main) { activity in
        guard let activity = activity else { return }

        if activity.walking {
            print("用户在走路")
        } else if activity.running {
            print("用户在跑步")
        } else if activity.cycling {
            print("用户在骑车")
        } else if activity.automotive {
            print("用户在开车")
        }

        // confidence 表示检测的可信度
        print("可信度: \(activity.confidence.rawValue)")
    }
}

最佳实践

  • 在请求 Core Motion 数据前先检查 isStepCountingAvailable() / isActivityAvailable()——不是所有设备都有运动传感器(比如 iPod touch)
  • HealthKit 和 Core Motion 的权限要分开请求,给用户一个清晰的理由说明为什么需要每个权限
  • 步态分析类 App 优先使用 CMPedometer 的高级 API 而不是原始传感器数据,苹果的算法比你手写的信号处理更可靠
  • watchOS 上长时间的运动数据采集要在 Workout Session 内进行,否则系统可能会限制后台传感器访问

还有什么值得关注

  • CMAltimeter 可以检测楼层攀升数据,对于健身 App 的爬楼梯追踪很有用
  • iOS 15 的 CMFallDetector(如果可用)可以辅助实现跌倒检测功能
  • HealthKit 的运动数据可以写入 HKWorkoutRoute 来记录 GPS 轨迹,配合运动类型数据一起展示
WWDC 2021