오늘날 스마트폰에 내장된 다양한 센서를 이용하여 각종 센싱 어플리케이션을 구동할 수 있다.
- Physical Activity
- Transportation Mode
- Plcae/Environment Context
- Conversation, Human Voice
모바일 센싱 디자인의 공통적인 패턴을 살펴보면
관찰되는 데이터들(raw data)를 모아서 특징을 잡아 최종적인 결과가 나오면
이를 바탕으로 서비스를 제공한다.
보통 머신러닝에서 활용하는 feature extraction 방식으로 학습이 선행되어야 한다.
학습된 모델을 바탕으로 데이터가 들어왔을 때 이 데이터가 어떤 것인지 답을 주는 방식이다.
이번 시간에는 Physical Activity를 Sensing하는 과정 및 방법에 대해 살펴볼 것이다.
Physical Activity의 예를 살펴보면
Activity의 종류에는 앉기, 서있기, 걷기, 뛰기 등이 있다.
각각에 따른 가속도 센서의 데이터의 변화를 살펴보자.
데이터를 살펴보면
구체적인 특징은 정적인 활동이 동적인 활동보다 데이터의 변화폭이 적다.
또한 Activity에 따라 각각 데이터가 가지는 특징이 다르다는 것도 확인할 수 있다.
이를 바탕으로 다양한 방법으로 특징을 뽑아내어 학습시키는 것이다.
기존의 머신러닝에서는 어떤 모델을 사용하냐는 것도 중요하지만
어떤 특징을 잘 잡아내서 학습을 시키느냐가 제일 중요한 이슈였다.
하지만 오늘날 머신러닝은 따로 특징을 뽑지 않고 raw data를 입력하면
스스로 특징을 찾아내 학습하는 방법을 많이 사용하고 있다.
결론적으로 학습된 모델을 바탕으로 현재 Physical Activity의 종류를 파악한다.
Mobile Sensing Pipline에 대한 예제를 바탕으로 더 이해해보자.
이번 시간에는 "가속도 센서 기반 활동량(r걸음 수) 모니터링" 예제를 구현해 볼 것이다.
가속도 센서를 사용하기 위해 SensorManager를 활용한다.
추후에 SensorManager에 관한 포스팅을 따로 준비할 예정이다.
이번 예제는 위와 같은 UI와 구조로 진행된다.
간단히 보자면 모니터링 시작 신호를 보내면 StepMonitor에서 계산을 한 후
계산이 완료되면 Broadcast를 전송해 MainActivity에서 결과를 출력하는 구조이다.
그러면 StepMonitor에서 어떻게 가속도 센서 데이터를 처리할지 생각해보자.
- Sensor data collection
- 3축 가속도 센서 데이터 중 y축 값만 수집한다.(핸드폰이 위아래로 움직인다.)
- Feature extraction
- 이전 가속도 센서 데이터 y축 값과 현재 y축 값의 차이 절대값 (걸으면 차이가 날 것이다.)
- Classification
- Step 유무 판단
- 센서 값이 변화가 일정 기준보다 크면 step이 있었다고 판단
- 작으면 없었다고 판단
- Step이 있었으면 step count를 증가시켜 UI에 보여준다.
- Step 유무 판단
이렇게 봤을 때 고려해야 할 문제가 3가지 정도 있다.
- 센서 데이터 중 y축 값만 보는 것으로 충분한가?
- 현재 값과 바로 이전 값의 차이를 보는 것으로 step을 구분하는 것이 적당한가?
- threshold 값을 어떻게 정할 것인가?
1. 센서 데이터 중 y축 값만 보는 것으로 충분한가?
우리가 핸드폰을 바탕으로 데이터 값을 얻어오기 때문에 항상 핸드폰이 기준이 된다.
먼저 핸드폰의 가속도 센서의 기준을 살펴보자.
이와 같은 축 방향을 가지고 있기 때문에 Y축이 위 아래로 움직일 때의 상태를 나타낼 수 있었다.
하지만 만약 사용자가 핸드폰을 가로로 넣어두거나, 기울어진 상태로 가지고 있다면
Y축의 데이터값이 정확하다고 할 수 있을까?
결론적으로 X, Y, Z축 모두 고려했을 때 더욱 정확한 측정이 가능하다는 것을 느낄 수 있다.
이제 한 걸음이 발생할 때 3축에서 일어나는 가속도 패턴을 살펴보자.
우선 전진방향(X축)으로 발생하는 가속도는 현재 움직이는 발이 다음 위치로 옮겨지기 직전에 가장 작은 값을 가지고,
걸음을 내딛어 몸을 끌어당길 때 가장 큰 값을 갖는다.
수직방향(Y축)으로 발생하는 가속도는 발을 움직이며 두 다리가 땅과 수직인 지점에서 정점을 찍고
걸음을 옮기며 비교적 다리가 벌어졌을 때 저점을 찍는다.
측면방향(Z축)은 직선운동에서 거의 변화가 없으므로 무시할 수준의 정보라고 판단할 수 있다.
실제로 달릴 떄를 가정한다면 Y축에 작용하는 힘이 강하기 때문에 Y축 가속도가 유난히 두드러질 수 있다.
하지만 걷는 상태를 가정한다면 오히려 X축 가속도가 더 두드러지게 달라질 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package kr.ac.koreatech.cse.msp11_stepmonitor;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView mAccelX;
private TextView mAccelY;
private TextView mAccelZ;
private TextView stepsText;
private int steps;
private BroadcastReceiver MyStepReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("kr.ac.koreatech.msp.stepmonitor")) {
steps = intent.getIntExtra("steps", 0);
stepsText.setText("steps: " + steps);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAccelX = (TextView)findViewById(R.id.accelX);
mAccelY = (TextView)findViewById(R.id.accelY);
mAccelZ = (TextView)findViewById(R.id.accelZ);
stepsText = (TextView)findViewById(R.id.count);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter("kr.ac.koreatech.msp.stepmonitor");
registerReceiver(MyStepReceiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(MyStepReceiver);
}
// Start/Stop 버튼을 눌렀을 때 호출되는 콜백 메소드
// Activity monitoring을 수행하는 service 시작/종료
public void onClick(View v) {
if(v.getId() == R.id.startMonitor) {
Intent intent = new Intent(this, StepMonitor.class);
startService(intent);
} else if(v.getId() == R.id.stopMonitor) {
stopService(new Intent(this, StepMonitor.class));
stepsText.setText("steps: " + 0);
}
}
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
package kr.ac.koreatech.cse.msp11_stepmonitor;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class StepMonitor extends Service implements SensorEventListener {
private static final String TAG = "StepMonitor";
private SensorManager mSensorManger;
private Sensor mAccel;
private float previousY, currentY;
private int threshold;
private int steps;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate()");
// step 여부를 판단하기 위한 y축 가속도 값의 차이의 문턱값
threshold = 10;
previousY = currentY = steps = 0;
mSensorManger = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccel = mSensorManger.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// SensorEventListener 등록
mSensorManger.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// intent: startService() 호출 시 넘기는 intent 객체
// flags: service start 요청에 대한 부가 정보. 0, START_FLAG_REDELIVERY, START_FLAG_RETRY
// startId: start 요청을 나타내는 unique integer id
Toast.makeText(this, "Step Monitor 시작", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
Toast.makeText(this, "Step Monitor 중지", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onDestroy()");
// SensorEventListener 해제
mSensorManger.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
// 센서 데이터가 업데이트 되면 호출
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
//***** sensor data collection *****//
// 현재 y축 가속도 값
currentY = event.values[1];
// simple step calculation
computeSteps(previousY, currentY);
// 현재 y축 가속도 값을 이전 y축 가속도 값으로 기억해 둠
previousY = event.values[1];
}
}
// a simple inference for step count
// 이전 y축 가속도 값과 현재 y축 가속도 값의 차가 threshold보다 크면 걸음 수 증가
private void computeSteps(float prev, float curr) {
//***** feature extraction *****//
// calculate feature data:
// 여기서는 이전 y축 가속도 값과 현재 y축 가속도 값의 차이를 이용
float feature = Math.abs(curr - prev);
//***** classification *****//
// check if there is a step or not:
// 여기서는 y축 가속도 값 차이가 일정 문턱값 이상이면 step으로 판단
if(feature > threshold) {
steps++;
// if steps increased, send steps data to MainActivity
Intent intent = new Intent("kr.ac.koreatech.msp.stepmonitor");
intent.putExtra("steps", steps);
sendBroadcast(intent);
}
}
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/startMonitor"
android:text="Start Step Monitoring"
android:textSize="20dp"
android:onClick="onClick"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/stopMonitor"
android:text="Stop Step Monitoring"
android:textSize="20dp"
android:onClick="onClick"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Accel: "
android:textSize="20dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/accelX"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/accelY"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/accelZ"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/count"
android:text="steps: 0"
android:textSize="20dp"/>
</LinearLayout>
|
cs |
'School Study > Mobile System Programming' 카테고리의 다른 글
GPS, WiFi를 이용한 위치 모니터링(Location Tracker) (0) | 2019.04.23 |
---|---|
Mobile Sensing Pipeline 2 (0) | 2019.04.23 |
Mobile Device Power Measurement (0) | 2019.04.16 |
Wifi Manager를 활용해 위치감지 앱 만들기 (0) | 2019.04.10 |
Power Manager 알아보기 (0) | 2019.04.10 |