Android 플랫폼은 기기의 위치를 확인할 수 있는 두 가지 센서, 즉 지자기장 센서와 가속도계를 제공합니다. Android 플랫폼은 기기의 전면이 물체에 얼마나 가까운지 확인할 수 있는 센서 (근접 센서라고 함)도 제공합니다. 지자기 센서와 근접 센서는 하드웨어 기반입니다. 대부분의 휴대전화 및 태블릿 제조업체는 지구 자기장 센서를 포함합니다. 마찬가지로 휴대전화 제조업체는 일반적으로 휴대전화가 사용자의 얼굴에 가까워지는지 확인하는 근접 센서를 포함합니다 (예: 통화 중). 기기의 방향을 결정하려면 기기의 가속도계와 지자기장 센서의 판독값을 사용할 수 있습니다.
참고: 방향 센서는 Android 2.2(API 수준 8)에서 지원 중단되었으며 방향 센서 유형은 Android 4.4W(API 수준 20)에서 지원 중단되었습니다.
위치 센서는 세계 좌표계에서 기기의 실제 위치를 확인하는 데 유용합니다. 예를 들어 지자기장 센서를 가속도계와 함께 사용하여 자북극을 기준으로 기기의 위치를 확인할 수 있습니다. 이러한 센서를 사용하여 애플리케이션의 참조 프레임에서 기기의 방향을 결정할 수도 있습니다. 위치 센서는 일반적으로 흔들림, 기울기, 추력과 같은 기기 움직임이나 모션을 모니터링하는 데 사용되지 않습니다 (자세한 내용은 움직임 감지 센서 참고).
지자기장 센서와 가속도계는 각 SensorEvent
에 대해 센서 값의 다차원 배열을 반환합니다. 예를 들어 지자기장 센서는 단일 센서 이벤트 중에 세 개의 좌표 축 각각에 대한 지자기장 세기 값을 제공합니다. 마찬가지로 가속도계 센서는 센서 이벤트 중에 기기에 적용된 가속을 측정합니다. 센서에서 사용하는 좌표계에 관한 자세한 내용은
센서 좌표계를 참고하세요. 근접 센서는 각 센서 이벤트에 대해 단일 값을 제공합니다. 표 1에는 Android 플랫폼에서 지원되는 위치 센서가 요약되어 있습니다.
표 1. Android 플랫폼에서 지원되는 위치 센서
센서 | 센서 이벤트 데이터 | 설명 | 측정 단위 |
---|---|---|---|
TYPE_GAME_ROTATION_VECTOR |
SensorEvent.values[0] |
x축의 회전 벡터 구성요소(x * sin(θ/2)). | 단위 없음 |
SensorEvent.values[1] |
y축의 회전 벡터 구성요소(y * sin(θ/2)). | ||
SensorEvent.values[2] |
z축의 회전 벡터 구성요소(z * sin(θ/2)). | ||
TYPE_GEOMAGNETIC_ROTATION_VECTOR |
SensorEvent.values[0] |
x축의 회전 벡터 구성요소(x * sin(θ/2)). | 단위 없음 |
SensorEvent.values[1] |
y축의 회전 벡터 구성요소(y * sin(θ/2)). | ||
SensorEvent.values[2] |
z축의 회전 벡터 구성요소(z * sin(θ/2)). | ||
TYPE_MAGNETIC_FIELD |
SensorEvent.values[0] |
x축의 지자기장 강도. | μT |
SensorEvent.values[1] |
y축의 지자기장 강도. | ||
SensorEvent.values[2] |
z축의 지자기장 강도. | ||
TYPE_MAGNETIC_FIELD_UNCALIBRATED |
SensorEvent.values[0] |
x축의 지자기장 강도(강철 보정 없음). | μT |
SensorEvent.values[1] |
y축의 지자기장 강도(강철 보정 없음). | ||
SensorEvent.values[2] |
z축의 지자기장 강도(강철 보정 없음). | ||
SensorEvent.values[3] |
x축의 철 편향 추정치. | ||
SensorEvent.values[4] |
y축의 철 편향 추정치. | ||
SensorEvent.values[5] |
z축의 철 편향 추정치. | ||
TYPE_ORIENTATION 1 |
SensorEvent.values[0] |
방위각(z축을 중심으로 한 각도). | 도 |
SensorEvent.values[1] |
경사(x축을 중심으로 한 각도). | ||
SensorEvent.values[2] |
롤(y축을 중심으로 한 각도). | ||
TYPE_PROXIMITY |
SensorEvent.values[0] |
객체와의 거리입니다.2 | cm |
1이 센서는 Android 2.2 (API 수준 8)에서 지원 중단되었으며 이 센서 유형은 Android 4.4W (API 수준 20)에서 지원 중단되었습니다. 센서 프레임워크는 기기 방향을 획득하는 다른 방법을 제공하며, 이는 기기 방향 계산에서 설명합니다.
2 일부 근접 센서는 가까움과 멀음을 나타내는 바이너리 값만 제공합니다.
게임 회전 벡터 센서 사용
게임 회전 벡터 센서는 지자기장을 사용하지 않는 점을 제외하고 회전 벡터 센서와 동일합니다. 따라서 Y축은 북쪽을 가리키는 대신 몇몇 다른 참조를 가리킵니다. 이러한 참조는 Z축 기준의 자이로스코프 드리프트와 같은 자릿수로 드리프트할 수 있습니다.
게임 회전 벡터 센서는 자기장을 사용하지 않으므로 상대 회전이 더 정확하고 자기장 변화의 영향을 받지 않습니다. 북쪽이 어디에 있는지 신경 쓰지 않고, 일반 회전 벡터가 자기장을 사용하기 때문에 요구사항에 맞지 않는 경우 게임에서 이 센서를 사용하세요.
다음 코드는 기본 게임 회전 벡터 센서의 인스턴스를 가져오는 방법을 보여줍니다.
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
자바
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
지자기 회전 벡터 센서 사용
지자기 회전 벡터 센서는 회전 벡터 센서와 유사하지만 자이로스코프를 사용하지 않습니다. 이 센서의 정확도는 일반 회전 벡터 센서보다 낮지만 전력 소모는 줄어듭니다. 배터리를 너무 많이 사용하지 않고 백그라운드에서 회전 정보를 수집하려는 경우에만 이 센서를 사용하세요. 이 센서는 일괄 처리와 함께 사용할 때 가장 유용합니다.
다음 코드는 기본 지자기 회전 벡터 센서의 인스턴스를 가져오는 방법을 보여줍니다.
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)
자바
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);
기기 방향 계산
기기의 방향을 계산하면 지구의 참조 프레임 (특히 자북극)을 기준으로 기기의 위치를 모니터링할 수 있습니다. 다음 코드는 기기의 방향을 계산하는 방법을 보여줍니다.
Kotlin
private lateinit var sensorManager: SensorManager ... // Rotation matrix based on current readings from accelerometer and magnetometer. val rotationMatrix = FloatArray(9) SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading) // Express the updated rotation matrix as three orientation angles. val orientationAngles = FloatArray(3) SensorManager.getOrientation(rotationMatrix, orientationAngles)
자바
private SensorManager sensorManager; ... // Rotation matrix based on current readings from accelerometer and magnetometer. final float[] rotationMatrix = new float[9]; SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading); // Express the updated rotation matrix as three orientation angles. final float[] orientationAngles = new float[3]; SensorManager.getOrientation(rotationMatrix, orientationAngles);
시스템은 기기의 가속도계와 함께 기기의 지자기장 센서를 사용하여 방향 각도를 계산합니다. 이 두 가지 하드웨어 센서를 사용하여 시스템은 다음 세 가지 방향 각도에 관한 데이터를 제공합니다.
- 방위각(-z축을 중심으로 한 회전 각도). 기기의 현재 나침반 방향과 자북극 사이의 각도입니다. 기기의 상단 가장자리가 자북을 향하면 방위각은 0도이고, 상단 가장자리가 남쪽을 향하면 방위각은 180도입니다. 마찬가지로 상단 가장자리가 동쪽을 향하면 방위각은 90도이고 상단 가장자리가 서쪽을 향하면 방위각은 270도입니다.
- 피치 (x축을 중심으로 한 회전 각도) 기기 화면에 평행한 평면과 지면에 평행한 평면 사이의 각도입니다. 기기를 바닥과 평행을 이루도록 하여 하단 가장자리가 사용자에게 가장 가까이 있도록 들고 기기의 상단 가장자리를 바닥쪽으로 기울이면 기울기 각도가 양수가 됩니다. 반대 방향으로 기울이면(기기 상단 가장자리가 지면에서 멀어짐) 기울기 각도가 음수가 됩니다. 값의 범위는 -90도에서 90도입니다.
- 롤 (Y축을 중심으로 한 회전 각도) 기기 화면에 수직인 평면과 지면에 수직인 평면 사이의 각도입니다. 기기를 바닥과 평행을 이루도록 하여 하단 가장자리가 사용자에게 가장 가까이 있도록 들고 기기의 왼쪽 가장자리를 바닥쪽으로 기울이면 롤 각도가 양수가 됩니다. 반대 방향으로 기울이면(기기의 오른쪽 가장자리를 땅 쪽으로 이동) 롤 각도가 음수가 됩니다. 값의 범위는 -180도에서 180도입니다.
참고:센서의 롤 정의가 지오센서 생태계의 대다수 구현을 반영하도록 변경되었습니다.
이러한 각도는 항공학에서 사용되는 각도 (요, 피치, 롤)와는 다른 좌표계를 사용합니다. 항공 시스템에서 x축은 항공기의 긴 면을 따라 뒤에서 앞으로 형성됩니다.
방향 센서는 가속도계와 지자기장 센서의 원시 센서 데이터를 처리하여 데이터를 가져옵니다. 관련된 대규모 처리로 인해 방향 센서의 정확성과 정밀도가 저하됩니다. 특히 이 센서는 롤 각도가 0인 경우에만 신뢰할 수 있습니다. 그 결과 방향 센서는 Android 2.2 (API 수준 8)에서 지원 중단되었고 방향 센서 유형은 Android 4.4W (API 수준 20)에서 지원 중단되었습니다.
방향 센서의 원시 데이터를 사용하는 대신 다음 코드 샘플과 같이 getRotationMatrix()
메서드를 getOrientation()
메서드와 함께 사용하여 방향 값을 계산하는 것이 좋습니다. 이 프로세스의 일환으로 remapCoordinateSystem()
메서드를 사용하여 방향 값을 애플리케이션의 참조 프레임으로 변환할 수 있습니다.
Kotlin
class SensorActivity : Activity(), SensorEventListener { private lateinit var sensorManager: SensorManager private val accelerometerReading = FloatArray(3) private val magnetometerReading = FloatArray(3) private val rotationMatrix = FloatArray(9) private val orientationAngles = FloatArray(3) public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do something here if sensor accuracy changes. // You must implement this callback in your code. } override fun onResume() { super.onResume() // Get updates from the accelerometer and magnetometer at a constant rate. // To make batch operations more efficient and reduce power consumption, // provide support for delaying updates to the application. // // In this example, the sensor reporting delay is small enough such that // the application receives an update before the system checks the sensor // readings again. sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer -> sensorManager.registerListener( this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) } sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField -> sensorManager.registerListener( this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) } } override fun onPause() { super.onPause() // Don't receive any more updates from either sensor. sensorManager.unregisterListener(this) } // Get readings from accelerometer and magnetometer. To simplify calculations, // consider storing these readings as unit vectors. override fun onSensorChanged(event: SensorEvent) { if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size) } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size) } } // Compute the three orientation angles based on the most recent readings from // the device's accelerometer and magnetometer. fun updateOrientationAngles() { // Update rotation matrix, which is needed to update orientation angles. SensorManager.getRotationMatrix( rotationMatrix, null, accelerometerReading, magnetometerReading ) // "rotationMatrix" now has up-to-date information. SensorManager.getOrientation(rotationMatrix, orientationAngles) // "orientationAngles" now has up-to-date information. } }
자바
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private final float[] accelerometerReading = new float[3]; private final float[] magnetometerReading = new float[3]; private final float[] rotationMatrix = new float[9]; private final float[] orientationAngles = new float[3]; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. // You must implement this callback in your code. } @Override protected void onResume() { super.onResume(); // Get updates from the accelerometer and magnetometer at a constant rate. // To make batch operations more efficient and reduce power consumption, // provide support for delaying updates to the application. // // In this example, the sensor reporting delay is small enough such that // the application receives an update before the system checks the sensor // readings again. Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (accelerometer != null) { sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); if (magneticField != null) { sensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } } @Override protected void onPause() { super.onPause(); // Don't receive any more updates from either sensor. sensorManager.unregisterListener(this); } // Get readings from accelerometer and magnetometer. To simplify calculations, // consider storing these readings as unit vectors. @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.length); } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.length); } } // Compute the three orientation angles based on the most recent readings from // the device's accelerometer and magnetometer. public void updateOrientationAngles() { // Update rotation matrix, which is needed to update orientation angles. SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading); // "rotationMatrix" now has up-to-date information. SensorManager.getOrientation(rotationMatrix, orientationAngles); // "orientationAngles" now has up-to-date information. } }
일반적으로 센서의 좌표계를 애플리케이션의 참조 프레임으로 변환하는 것 외에는 기기의 원시 방향 각도에 대한 데이터 처리나 필터링을 실행할 필요가 없습니다.
지자기장 센서 사용
지자기장 센서를 사용하면 지구 자기장의 변화를 모니터링할 수 있습니다. 다음 코드는 기본 지구 자기장 센서의 인스턴스를 가져오는 방법을 보여줍니다.
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
자바
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
참고: 앱이 Android 12 (API 수준 31) 이상을 타겟팅하는 경우 이 센서는 비율 제한됩니다.
이 센서는 세 개의 각 좌표축에 대해 원시 필드 강도 데이터 (μT 단위)를 제공합니다.
일반적으로는 이 센서를 직접 사용할 필요가 없습니다. 대신 회전 벡터 센서를 사용하여 원시 회전 운동을 결정하거나 가속도계와 지자기장 센서를 getRotationMatrix()
메서드와 함께 사용하여 회전 행렬과 경사 행렬을 얻을 수 있습니다. 그런 다음 getOrientation()
및 getInclination()
메서드와 함께 이러한 행렬을 사용하여 방위각 및 지구 자기장 기울기 데이터를 얻을 수 있습니다.
참고: 앱을 테스트할 때 기기를 8자 모양으로 흔들면 센서의 정확성을 개선할 수 있습니다.
보정되지 않은 자기계 사용
미보정 자기계는 자기장에 강철 보정이 적용되지 않는다는 점을 제외하고 지자기장 센서와 유사합니다. 공장 보정 및 온도 보상은 여전히 자기장에 적용됩니다. 미보정 자기계는 잘못된 강철 예상을 처리하는 데 유용합니다. 일반적으로 geomagneticsensor_event.values[0]
은 uncalibrated_magnetometer_event.values[0] -
uncalibrated_magnetometer_event.values[3]
에 가깝습니다. 즉, 다음과 같습니다.
calibrated_x ~= uncalibrated_x - bias_estimate_x
참고: 미보정 센서는 좀 더 원시적인 결과를 제공하며 일부 바이어스를 포함할 수 있습니다. 하지만 측정값에는 보정을 통해 적용된 교정값의 '점프'가 훨씬 적게 포함됩니다. 일부 애플리케이션은 좀 더 매끄럽고 안정적인 미보정 결과를 선호할 수 있습니다. 예를 들어 애플리케이션이 자체 센서 융합을 수행하려고 시도하는 경우 보정을 도입하면 오히려 결과가 왜곡될 수 있습니다.
미보정 자기계는 자기장 외에도 각 축의 예상 강철 편향을 제공합니다. 다음 코드는 보정되지 않은 기본 자기장의 인스턴스를 가져오는 방법을 보여줍니다.
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED)
자바
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);
근접 센서 사용
근접 센서를 통해 객체가 기기에서 얼마나 멀리 떨어져 있는지 확인할 수 있습니다. 다음 코드는 기본 근접 센서의 인스턴스를 가져오는 방법을 보여줍니다.
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
자바
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
근접 센서는 일반적으로 사용자의 머리가 휴대전화 기기의 화면에서 얼마나 떨어져 있는지 확인하는 데 사용됩니다 (예: 사용자가 전화를 걸거나 받을 때). 대부분의 근접 센서는 절대 거리(cm)를 반환하지만 일부는 근접 및 먼 값만 반환합니다.
참고: 일부 기기 모델의 경우 근접 센서가 화면 아래에 있어 화면이 켜져 있는 동안 사용 설정하면 화면에 깜박이는 점이 표시될 수 있습니다.
다음 코드는 근접 센서의 사용 방법을 나타냅니다.
Kotlin
class SensorActivity : Activity(), SensorEventListener { private lateinit var sensorManager: SensorManager private var proximity: Sensor? = null public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) // Get an instance of the sensor service, and use that to get an instance of // a particular sensor. sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do something here if sensor accuracy changes. } override fun onSensorChanged(event: SensorEvent) { val distance = event.values[0] // Do something with this sensor data. } override fun onResume() { // Register a listener for the sensor. super.onResume() proximity?.also { proximity -> sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL) } } override fun onPause() { // Be sure to unregister the sensor when the activity pauses. super.onPause() sensorManager.unregisterListener(this) } }
자바
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private Sensor proximity; @Override public final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Get an instance of the sensor service, and use that to get an instance of // a particular sensor. sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); } @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. } @Override public final void onSensorChanged(SensorEvent event) { float distance = event.values[0]; // Do something with this sensor data. } @Override protected void onResume() { // Register a listener for the sensor. super.onResume(); sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause() { // Be sure to unregister the sensor when the activity pauses. super.onPause(); sensorManager.unregisterListener(this); } }
참고: 일부 근접 센서는 'near' 또는 'far'를 나타내는 바이너리 값을 반환합니다. 이 경우 센서는 일반적으로 far 상태에서 최대 범위 값을 보고하고 near 상태에서는 더 작은 값을 보고합니다. 일반적으로 원거리 값은 5cm를 초과하는 값이지만 센서마다 다를 수 있습니다. getMaximumRange()
메서드를 사용하여 센서의 최대 범위를 확인할 수 있습니다.