加速度センサでスクワットアプリを作ってみよう
- - PR -
ここからはスクワットアプリを作成します。作成するアプリは以下の仕様を満たすものとします。
- デバイスを手で持ってスクワットをすることによって、スクワットの回数をカウントできる
- スクワット中、デバイスがどのような姿勢であってもカウントできること
- スタートボタンをタップするとカウントが始まり、ストップボタンをタップするとカウントが止まってリセットされる
- スクワットは立った状態からスタートするものとする
- 立った状態から体を屈め、再び立った際にカウントが1回増える
スクワットの回数をカウントするためには、立った状態から屈んだ状態になったという判定と、屈んだ状態から立った状態になったという判定が必要です。今回は、スクワットの上下運動の終わり際の減速に伴う鉛直方向の加速度を、加速度センサで検知して判定したいと思います。
例えば、立った状態から屈んだ状態になる場合は、屈み終わる直前に減速するため下方向の加速度が掛かります。そこで、この下方向の加速度が一定時間一定値を超えていた場合に屈んだ状態になったと判定します。しかし、ユーザーによって与えられる加速度(以下、「ユーザー加速度」)は3次元空間のどの方向を向いているか分からないので、ユーザー加速度のうち下方向の成分のみを取り出して判定する必要があります。
そこで、Core Motionから取得できる重力加速度を利用してユーザー加速度から重力加速度と同じ方向の成分のみを取り出し、下方向の加速度として判定に利用します。この下方向の加速度が0.2秒以上0.15Gを超えていた場合に屈んだ状態になったと判定します。
立ち上がる場合はこの逆で、上方向の加速度で判定します。この場合は加速度の方向が重力と逆になるので、重力方向の加速度の値が負の値になります。よって、この値が0.2秒以上-0.15Gを下回っていた場合に立った状態になったと判定します。
■ アプリのUI
下のスクリーンショットは、作成したアプリを実行したときの様子です。
スタートボタンを押した後、デバイスを持って屈むと矢印のイメージが上向きになり、立ち上がると矢印のイメージが再び下向きになって回数がカウントされます。
■ ソースコードは、こちら
では、ソースコードの主要な部分を見ていきます。ここではすべてのソースコードを掲載しませんが、完成したアプリのソースコードが、こちらからダウンロードできるので参考にしてください。
■ CMMotionManagerのインスタンスを生成
まずは、viewDidLoadメソッド内のCMMotionManagerを生成している部分のソースコードです。
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.1;
CMMotionManagerのインスタンスを生成し、モーションデータの更新間隔を0.1秒ごとに設定しています。
■ デバイスモーション用のプロパティを使う
このアプリでは、ユーザー加速度と重力加速度を分離して取得する必要があるので、加速度センサ単体ではなくジャイロスコープなども利用するデバイスモーションを使用します。そのため、モーションデータの更新間隔の設定には、accelerometerUpdateIntervalプロパティではなく「deviceMotionUpdateInterval」プロパティを使用しています。このアプリでは、モーションデータの測定開始メソッドなどもすべてデバイスモーション用のものを使用します。
■ データ取得開始処理
- (void)startCounting
{
// モーションデータ更新時のハンドラを作成
void (^handler)(CMDeviceMotion *, NSError *) = ^(CMDeviceMotion *motion, NSError *error)
{
// ユーザー加速度の重力方向の大きさを算出
double magnitude = [self gravityDirectionMagnitudeForMotion:motion];
// 算出したユーザー加速度の重力方向の大きさからスクワットの動きを判定
[self validateGravityDirectionMagnitude:magnitude];
};
// モーションデータの測定を開始
NSOperationQueue *queue = [NSOperationQueue currentQueue];
[motionManager startDeviceMotionUpdatesToQueue:queue withHandler:handler];
count = 0;
isCounting = YES;
[self changeViewState];
isSitting = YES;
[self updateViewAnimated:NO];
}
まず、モーションデータ更新時の処理を記述したハンドラブロックを作成しています。このブロックからモーションデータの処理メソッドを呼び出して、デバイスに掛かっている加速度を判定しています。
次に、モーションデータ更新時に呼び出されるブロックを実行する「NSOperationQueue」を取得します。currentQueueを取得することによって、ブロックがメインスレッドで実行されるようにしています。
最後に、CMMotionManagerのstartDeviceMotionUpdatesToQueue:withHandler:メソッドを呼び出して、モーションデータの測定を開始しています。このメソッドの2番目のパラメータで先ほどのブロックを渡して、モーションデータ更新時に呼び出されるようにします。
■ データ更新処理
続いて、モーションデータ更新時の処理を見ていきます。以下は、ユーザー加速度の重力方向における大きさを求める部分です。
- (double)gravityDirectionMagnitudeForMotion:(CMDeviceMotion *)motion
{
// ユーザー加速度の測定値を取得
CMAcceleration user = motion.userAcceleration;
// 重力加速度の測定値を取得
CMAcceleration gravity = motion.gravity;
// ユーザー加速度の大きさを算出
double magnitude = sqrt(pow(user.x, 2) + pow(user.y, 2) + pow(user.z, 2));
// ユーザー加速度のベクトルと重力加速度のベクトルのなす角θのcosθを算出
double cosT = (user.x * gravity.x + user.y * gravity.y + user.z * gravity.z) /
sqrt((pow(user.x, 2) + pow(user.y, 2) + pow(user.z, 2)) *
(pow(gravity.x, 2) + pow(gravity.y, 2) + pow(gravity.z, 2)));
// ユーザー加速度の大きさにcosθを乗算してユーザー加速度の重力方向における大きさを算出し、小数点第3位で丸める
double gravityDirectionMagnitude = round(magnitude * cosT * 100) / 100;
return gravityDirectionMagnitude;
}
このメソッドはモーションデータ更新時に実行されるブロック内から呼び出されています。ここでは、ユーザー加速度の大きさを求め、その重力方向の成分を算出しています。
■ 立ち判定と座り判定
- (void)validateGravityDirectionMagnitude:(double)magnitude
{
if (timer.isValid)
{
// 立ち判定もしくは座り判定中の場合
if ((!isSitting && isDecelerationStarted && magnitude > -kUserAccelerationThreshold) ||
(isSitting && isDecelerationStarted && magnitude < kUserAccelerationThreshold))
{
// ユーザー加速度の重力方向における大きさが閾値を下回る場合、判定をキャンセル
[timer invalidate];
timer = nil;
NSLog(@"timer is canceled.");
}
}
else
{
if (!isSitting && magnitude < -kUserAccelerationThreshold)
{
// 立ちフェイズかつユーザー加速度の重力方向における大きさが閾値を上回る場合、立ち判定スタート
// 一定時間閾値を上回っていた場合に、立ち判定成立
timer = [NSTimer scheduledTimerWithTimeInterval:kDecelerationTime
target:self
selector:@selector(timerHandler:)
userInfo:nil
repeats:NO];
isDecelerationStarted = YES;
NSLog(@"standing timer start.");
}
else if (isSitting && magnitude > kUserAccelerationThreshold)
{
// 座りフェイズかつユーザー加速度の重力方向における大きさが閾値を上回る場合、座り判定スタート
// 一定時間閾値を上回っていた場合に、座り判定成立
timer = [NSTimer scheduledTimerWithTimeInterval:kDecelerationTime
target:self
selector:@selector(timerHandler:)
userInfo:nil
repeats:NO];
isDecelerationStarted = YES;
NSLog(@"sitting timer start.");
}
}
}
このメソッドも、モーションデータ更新時のハンドラブロックから呼び出されています。引数「magnitude」には、先ほど算出したユーザー加速度の重力方向における大きさが渡されています。ユーザー加速度の重力方向における大きさが、しきい値を上回る場合にタイマーをスタートさせ、タイマーが終了するまでにユーザー加速度の重力方向における大きさが、しきい値を下回らなければ判定を成立させています。
続いて次ページでは、ジャイロスコープを利用したアプリを作成してみましょう。しかし、ジャイロスコープ単体で取得できるデータでアプリを作成するのは、なかなか難しいものがあります。そこで、ジャイロスコープと加速度センサを組み合わせることによって測定できる「デバイスの姿勢」を利用して腹筋アプリを作成します。
1-2-3 |
INDEX | ||
iPhone/iPadスマートアプリ開発レシピ(2) 加速度センサとジャイロで体の動きを感じるアプリを作る |
||
Page1 モーションセンサでスマート度アップ! いまさら聞けない「加速度センサ」とは いまさら聞けない「ジャイロスコープ」とは モーションデータを扱うiOSの「Core Motion」 Core Motionを利用した加速度データの取得 Core Motionを利用したジャイロデータの取得 |
||
Page2 加速度センサでスクワットアプリを作ってみよう |
||
Page3 デバイスの姿勢を利用した腹筋アプリを作ってみよう Core Motionなら簡単にモーションデータを使える |
Smart&Social フォーラム トップページへ |
- 夏休みの自由研究にマイコンボードで「電子サイコロ」を作ったり、音楽プログラミングをしたりしてみよう (2017/7/24)
子ども向け電子工作&プログラミング用マイコンボード「chibi:bit」の基本的な使い方を紹介する企画。夏休みの自由研究に「電子サイコロ」を作ったり、音楽プログラミングをしたりしてみよう - 子ども向け電子工作&プログラミング用マイコンボード「chibi:bit」の基本的な使い方 (2017/7/20)
子ども向け電子工作&プログラミング用マイコンボード「chibi:bit」の基本的な使い方を紹介する。夏休みの子どもの自由研究などに役立てつつ、プログラミングを始めるきっかけにしてみてはいかがだろうか - 3DゲームのAIをiOSのSceneKitとGameplayKitで作る基本 (2017/7/10)
3Dゲーム用のフレームワークSceneKitを使った簡単なアプリ制作を通して、3Dゲーム用の人工知能(AI)について学ぶ - UnityアプリをWebGL、UWP、Android、iOS用としてビルドしてみた (2017/6/27)
アプリをWebで実行できるように書き出す方法やWindows上でUWP、Android、iOS用などにビルドする方法について解説する【Windows 10、Unity 5.6に対応】
|
|