[Swift初心者]AirPods Proの加速度センサーで首の動き(ヘドバン)を可視化してみた
AirPods Proの加速度センサーで首の動き(ヘドバン)を可視化してみた
はじめに
AirPods Proには加速度センサーが搭載されていることをご存じでしょうか?
このセンサーを活用することで、頭の動きや首の振り(いわゆるヘドバン)の勢いをiPhoneでリアルタイムに可視化できます。
本記事では、AirPods Proから取得した加速度データを使って、**「首を振る動きの擬似的な速度(km/h風)」**をSwiftUIで表示するシンプルなプロトタイプアプリを紹介します。
将来的には、動きの激しさに応じてエフェクトを加えたり、簡単なゲームに発展させたりする予定ですが、今回はまず、センサーからの加速度取得と表示までをゴールにしています。
完成イメージ
使用環境
- iOS 18.5(実機でのみ動作)
- Swift 5.9
- SwiftUI
- AirPods Pro(耳に装着した状態で使用)
アプリ概要とアプローチ
AirPods Proから CMHeadphoneMotionManager
を使って加速度データを取得します。
取得できるのは userAcceleration
(重力成分を除いた人の動きのみ)で、3軸(x, y, z)の値を使って加速度ベクトルの大きさを計算し、それを元に速度風の数値として表示します。
速度の計算は正確な物理的速度ではなく、擬似的に「爆速感」を演出するためのスケーリングを行っています。
準備
CoreMotionフレームワークが使えるように、Info.plistにNSMotionUsageDescription
キーを追加し、説明を記載します。(これをしないと加速度データが取得できない)
ViewModelの実装:加速度データの取得と速度換算
この HeadTrackingManager
クラスは、AirPods Pro の加速度センサーから取得したデータを用いて、首の動きの勢いを“速度風”の数値としてリアルタイムに表示するためのViewModelです。
全体の処理の流れは以下の通りです:
-
startTracking()
を呼ぶと、AirPods Proからのモーションデータの取得が開始されます。 -
motion.userAcceleration
を使って、3軸の加速度を取得します。 - 加速度の大きさをユークリッド距離で計算し、簡易的にスケーリングして「km/h」のような数値に変換します。
- 画面表示用の
@Published
プロパティ(currentSpeedKmh
)を更新し、最大記録も同時に保持します。 -
stopTracking()
を呼ぶと、センサーデータの取得を停止します。
以下が実際の実装コードです:
import CoreMotion
import Foundation
class HeadTrackingManager: ObservableObject {
private var motionManager = CMHeadphoneMotionManager()
@Published var currentSpeedKmh: Double = 0.0
@Published var maxSpeedKmh: Double = 0.0
func startTracking() {
// AirPodsのモーションデータが利用可能か確認
guard motionManager.isDeviceMotionAvailable else {
print("Headphone motion data is not available.")
return
}
motionManager.startDeviceMotionUpdates(to: .main) { [weak self] motion, error in
guard let self = self, let motion = motion else {
print("Error: \(String(describing: error))")
return
}
let acc = motion.userAcceleration
// ユークリッド距離(加速度の大きさ)を計算
let magnitude = sqrt(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z)
// 擬似的に m/s 扱いして km/h に変換(厳密な計算は大変なため簡略化)
let speedKmh = magnitude * 30.0
// 最大値を更新
DispatchQueue.main.async {
self.currentSpeedKmh = speedKmh
if speedKmh > self.maxSpeedKmh {
self.maxSpeedKmh = speedKmh
}
}
}
}
func stopTracking() {
motionManager.stopDeviceMotionUpdates()
}
}
擬似速度について
本来、加速度から正確な速度を求めるには積分処理が必要です。
しかし、センサーの誤差やデータ間隔のばらつきによって累積誤差が大きくなりやすいため、今回はシンプルに以下の式で代用しています:
// 擬似的に m/s 扱いして km/h に変換(厳密な計算は大変なため簡略化)
let speedKmh = magnitude * 30.0
精密な積分処理ではありませんが、「激しく動いたら値が大きくなる」という意図で調整しています。
コード:UI
SwiftUIを使って以下のようなUIで表示しています:
- 現在の擬似速度(km/h)
- 最大加速度記録
- スタート / ストップボタン
今後はこのUIにランク表示やエフェクトを追加予定です。
import SwiftUI
struct ContentView: View {
@StateObject private var tracker = HeadTrackingManager()
var body: some View {
VStack(spacing: 24) {
Text("現在のヘドバン速度")
.font(.title2)
Text(String(format: "%.2f km/h", tracker.currentSpeedKmh))
.font(.largeTitle)
.bold()
.foregroundColor(.blue)
Text("最大記録")
.font(.title3)
Text(String(format: "%.2f km/h", tracker.maxSpeedKmh))
.font(.title)
.foregroundColor(.red)
Button("開始") {
tracker.startTracking()
}
.padding()
.frame(width: 150)
.frame(height: 50)
.background(Color.green)
.foregroundColor(.white)
.clipShape(Capsule())
Button("停止") {
tracker.stopTracking()
}
.padding()
.frame(width: 150)
.frame(height: 50)
.background(Color.gray)
.foregroundColor(.white)
.clipShape(Capsule())
}
.padding()
}
}
#Preview {
ContentView()
}
実行時の確認ポイント
-
AirPods Proを耳に装着し、アプリを実機で起動
-
首を振ると、画面上の「km/h」がリアルタイムで変化
-
記録された最大加速度も保持される
今後の予定
-
擬似速度に応じて「S級!A級!」などのランクを表示
-
スタート〜測定〜結果表示までのゲーム風フロー(ゲームセンターにある、パンチ力測定マシンのヘッドバンキングの速度版作りたい)
-
効果音(メタルBGM)やアニメーションエフェクト
-
SwiftUIでのシンプルなゲームUIの追加
終わりに
この記事では、SwiftのApple純正フレームワークであるCoreMotionを用いて、AirPods Proの加速度センサーの情報を取得し可視化するところまでを紹介しました。
普段使用しているデバイスの中に搭載されているセンサーで遊ぶのは非常に面白く、趣味や研究など幅広い用途で活用できると思います。
著者について
地方国立大学で情報学を学んでいる学生です。研究室はデータ分析系のところに所属しています。
個人でiOSアプリの開発を行なって遊んでいます(その場で思いついたようなくだらないものしか作ってないです)
普段はSwiftUIを中心に、遊び感覚で小さなiOSアプリを作っています。この記事もその一環です。
#iOS #SwiftUI #CoreMotion #AirPodsPro #個人開発
Discussion