개요 및 설명
Android는 핸드폰 내부에 존재하는 Sensor를 통해 현재 위치, 가속도, 방향 등을 감지한다.
Android studio의 SensorManager를 통해 이 정보들을 가져와서 사용할 수 있고, 다듬어서 원하는 정보로 변형도 가능하다. 예를 들어 가속도와 자기장 센서를 활용하여 나침반을 구현할 수 있다.
별 헤는 밤 앱은 별자리 관광 애플리케이션이지만 별 관측에 직접적으로 도움을 줄 수 있는 서비스가 부족했었다. 그래서 이번 업데이트 때 이 센서 정보를 활용하여 별자리의 방위각과 고도를 찾아줄 수 있는 기능을 추가하기로 했다!
이때 별자리의 고도와 핸드폰의 기울기로 비교해야 하므로 정확히 말하면 핸드폰의 기울기를 구한다고 하는 것이 올바른 표현이다. 직접적으로 방위각과 고도를 계산하는 코드는 제외하고 (기업 비밀입니다..!) 센서에 관련된 코드만 설명하도록 하겠다.
Android.hardware.Sensor
정의: Android 기기의 움직임, 방향, 환경 조건을 측정하는 센서의 데이터를 가져오는 라이브러리를 의미한다.
다양한 센서 데이터들이 있는데, 이번에 활용할 데이터들만 간략하게 정리해 보도록 하겠다.
센서의 종류에는 크게 위치 센서, 동작 센서, 환경 센서 3가지로 나뉠 수 있다.
- 위치 센서: 지자기장, 방위각 등 기기의 물리적인 위치 정보를 감지하는 센서
- 동작 센서: 가속도, 가속력, 회전 벡터, 중력, 회전 속도 등 기기 동작을 감지하는 센서
- 환경 센서: 주변 기압, 기온, 온도, 습도 등 다양한 환경 데이터를 감지하는 센서
그리고 그 세 분류에서도 상세히 나뉘는데 우리가 집중적으로 활용할 센서는 3가지다.
- TYPE_ACCELEROMETER(가속도 센서)
- TYPE_MAGNETIC_FIELD (지자기장 센서)
*국제 기준 계에서 상대적인 기기의 위치를 활용하기 위해서는 이 2가지 센서가 필요하다.
- TYPE_GRAVITY(중력 센서)
*핸드폰의 기울어진 기울기를 계산하기 위해 중력 센서를 활용했다.
*각각의 센서의 x,y,z 축 데이터는 SensorEvent.values [0~2]로 표현하여 사용할 수 있다.
아래의 영상을 보면 각 x,y,z 값이 변화하는 것을 확인할 수 있다.
핸드폰이 움직을 때는 가속도 센서의 변화만 있고, 지자기장 센서는 변화가 없는 것을 볼 수 있다.
하지만 핸드폰의 기울기가 변화했을 때는 가속도 센서, 지자기장 센서 모두 급격하게 변화하는 것을 볼 수 있다.
여기서 기울기를 구하기 위해 중력 센서를 사용한 이유를 알 수 있다.
위치와 다르게 기울기는 중력 가속도와 외력의 영향을 받아 노이즈가 많아 정확한 측정이 어렵다. 따라서 이런 노이즈를 줄이기 위해서 별도로 중력 센서를 활용하게 되었다.
▶︎ 움직임 영상
▶︎ 회전 영상
이외에 다른 Sensor 데이터를 활용하고 싶다면 아래의 링크에서 참고하면 됩니다.
Android Sensor 활용
1. Sensor 선언
public class ExampleActivity implements SensorEventListener {
private SensorManager mSensorManger; //SensorManager
private Sensor mAcclerometer; // 가속도 센서
private Sensor mMagnetometer; //지자기장 센서
private Sensor mPressure; //중력 센서
private float[] mLastAcceleromter = new float[3]; //가속도 x,y,z축 리스트
private float[] mLastMagnetometer = new float[3];//지자기장 x,y,z축 리스트
private boolean mLastAccelerometerSet = false; //evenet.sensor 등록 여부
private boolean mLastMagnetometerSet = false;
private final float[] mOrientation = new float[3]; //Azimuth, Pitch, Roll
//Sensor 선언
mSensorManger = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mAcclerometer = mSensorManger.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mMagnetometer = mSensorManger.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mPressure = mSensorManger.getDefaultSensor(Sensor.TYPE_GRAVITY);
~ 내부 코드 ~
}
Android.hardware 패키지에서 가져오는 코드들이므로 별도로 build.gradle 파일에 추가할 dependencies는 없다.
우선 Android Sensor를 활용하기 위해서는 액티비티 선언 부분에서 SensorEventListener를 implements 해야 한다.
2. Sensor 등록
@Override
protected void onResume() { // 주기적으로 sensor data 갱신
super.onResume();
mSensorManger.registerListener((SensorEventListener) this, mAcclerometer,
SensorManager.SENSOR_DELAY_UI);
mSensorManger.registerListener((SensorEventListener) this, mMagnetometer,
SensorManager.SENSOR_DELAY_UI);
mSensorManger.registerListener((SensorEventListener) this, mPressure,
SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onPause() {// sensor data 갱신 중지
super.onPause();
mSensorManger.unregisterListener((SensorEventListener) this, mAcclerometer);
mSensorManger.unregisterListener((SensorEventListener) this, mMagnetometer);
}
- registerListener: 주기적으로 sensor data를 업데이트하여 받기 위한 리스너이다.
*registerListener의 맨 마지막 파라미터에 업데이트를 받는 주기(속도)를 지정할 수 있다.
Delay | 속도 | 특징 |
SENSOR_DELAY_FASTEST | 0 ms | 최대로 빠른 속도 |
SENSOR_DELAY_GAME | 20,000ms | 게임에 적합한 속도 |
SENSOR_DELAY_UI | 60,000ms | 사용자 인터페이스에 적합한 속도 |
SENSOR_DELAY_NORMAL | 200,000ms | 화면 방향 변경에 적합한 속도 (기본값) |
- unregisterListener: onPause는 액티비티가 정지한 경우에 실행되는 함수로 등록을 해제하여 sensor data의 업데이트를 중지한다.
3. 센서 값 활용
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER&&
event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
int axisX =SensorManager.AXIS_X;
int axisZ = SensorManager.AXIS_Z;
SensorManager.getRotationMatrix(**R**, I, mLastAcceleromter, mLastMagnetometer);
SensorManager.remapCoordinateSystem(R, axisX, axisZ, adjustedR);
SensorManager.getOrientation(R,Orientation);
~~ 방위각을 구하는 수학 공식 ~~
}
if(event.sensor.getType() == Sensor.TYPE_GRAVITY) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
~~ 고도를 구하는 수학 공식 ~~
}
}
- AXIS_X, AXIS_Z: 디바이스의 가로축, 세로축 값
- SensorManager.getRotationMatrix: 디바이스의 회전 매트릭스(rotation matrix) 값을 얻기 위한 메서드
- R: 회전 매트릭스를 저장할 배열로, 9개의 요소를 가지며, 회전 매트릭스의 값들이 여기에 저장됩니다.
- I: 기울기 매트릭스를 저장할 배열이며, 일반적으로 null로 전달됩니다.
- SensorManager.remapCoordinateSystem(): 센서 데이터의 좌표계를 재매핑하는 데 사용
**** 이 메서드를 사용한 이유는 핸드폰이 수평인 상태에서 수직으로 세우는 변화가 있는 경우가 있다.**
별 헤는 밤에서는 별을 촬영하기 위해 핸드폰을 하늘 위로 올려야 하는 상황이 생기게 되는데 이때 이 메서드가 없다면 핸드폰은 계속해서 수평인 상태로 센서 데이터를 수집하게 된다.
- SensorManager.getOrientation: 디바이스의 방향에 대한 방위각(azimuth), 피치(pitch), 롤(roll) 값을 계산하는 메서드
- Orientation: 방위각, 피치, 롤 값을 저장할 배열입니다.
** getOrientation에서 가져온 방위각, 피치, 롤 데이터는 raw data로 그대로 사용하기에는 정확도가 많이 떨어진다. 따라서 다양한 Math 함수들을 활용하여 다듬은 후에 사용해야 한다.
정리
Android의 Sensor에 대해 알아보고 활용법에 대해 정리해 보았다.
Sensor 데이터는 매우 민감하므로 정확한 센서 데이터를 구하기는 쉽지 않다. 따라서 딜레이 속도를 그에 맞게 조절하는 것과 그에 맞는 정확한 수학 공식이 필요하다.
만약 핸드폰의 정확한 방위각과 고도를 구하는 공식이 필요한 분들은 댓글을 남겨주시면 전달해 드리도록 하겠습니다.
'별밤 일지 > 개발' 카테고리의 다른 글
[Java] 리플렉션, ObjectMapper (2) | 2024.03.12 |
---|---|
[Springboot] Test With Mockito, JUnit (4) | 2024.03.05 |
[Spring] Logback 을 활용한 로깅하기 (0) | 2024.01.30 |
[QueryDSL] queryDSL 도입기 (0) | 2024.01.28 |
[Firebase Cloud Messaging]Android FCM 적용 (2) | 2024.01.21 |