움직임 감지 센서

Android 플랫폼은 기기의 움직임을 모니터링할 수 있는 여러 센서를 제공합니다.

센서의 가능한 아키텍처는 센서 유형에 따라 다릅니다.

  • 중력, 선형 가속, 회전 벡터, 중요한 움직임, 걸음수 측정기, 걸음 감지기 센서는 하드웨어 기반 또는 소프트웨어 기반입니다.
  • 가속도계 및 자이로스코프 센서는 항상 하드웨어 기반입니다.

대부분의 Android 지원 기기에는 가속도계가 있으며, 현재는 많은 기기에 자이로스코프가 포함되어 있습니다. 소프트웨어 기반 센서의 가용성은 데이터를 도출하기 위해 하나 이상의 하드웨어 센서에 의존하는 경우가 많기 때문에 더 가변적입니다. 기기에 따라 이러한 소프트웨어 기반 센서는 가속도계와 자기계 또는 자이로스코프에서 데이터를 가져올 수 있습니다.

움직임 감지 센서는 기울기, 흔들기, 회전, 스윙과 같은 기기 움직임을 모니터링하는 데 유용합니다. 움직임은 일반적으로 직접적인 사용자 입력 (예: 게임에서 자동차를 조종하는 사용자 또는 게임에서 공을 제어하는 사용자)을 반영하지만 기기가 있는 물리적 환경 (예: 자동차를 운전하는 동안 함께 이동)을 반영할 수도 있습니다. 첫 번째 경우에는 기기의 기준 프레임 또는 애플리케이션의 기준 프레임을 기준으로 모션을 모니터링하고, 두 번째 경우에는 전 세계의 기준 프레임을 기준으로 모션을 모니터링합니다. 움직임 감지 센서 자체는 일반적으로 기기 위치를 모니터링하는 데 사용되지 않지만, 지자기장 센서와 같은 다른 센서와 함께 사용하여 세계 기준 프레임에 상대적인 기기 위치를 확인할 수 있습니다 (자세한 내용은 위치 센서 참고).

모든 움직임 감지 센서는 각 SensorEvent에 대한 센서 값의 다차원 배열을 반환합니다. 예를 들어 단일 센서 이벤트 중에 가속도계는 3개의 좌표축에 대한 가속력 데이터를 반환하고 자이로스코프는 3개의 좌표축에 대한 회전 속도 데이터를 반환합니다. 이러한 데이터 값은 다른 SensorEvent 매개변수와 함께 float 배열(values)로 반환됩니다. 표 1에는 Android 플랫폼에서 사용할 수 있는 움직임 감지 센서가 요약되어 있습니다.

표 1. Android 플랫폼에서 지원되는 움직임 감지 센서

센서 센서 이벤트 데이터 설명 측정 단위
TYPE_ACCELEROMETER SensorEvent.values[0] x축을 따라 작용하는 가속력 (중력 포함)입니다. m/s2
SensorEvent.values[1] y축을 따른 가속력 (중력 포함)입니다.
SensorEvent.values[2] Z축을 따른 가속력 (중력 포함)
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] 바이어스 보상 없이 X축을 따라 측정된 가속도입니다. m/s2
SensorEvent.values[1] 바이어스 보상 없이 Y축을 따라 측정된 가속도입니다.
SensorEvent.values[2] 바이어스 보상 없이 Z축을 따라 측정된 가속입니다.
SensorEvent.values[3] 예상 바이어스 보상으로 X축을 따라 측정된 가속도입니다.
SensorEvent.values[4] 추정된 바이어스 보상으로 Y축을 따라 측정된 가속도입니다.
SensorEvent.values[5] 추정된 바이어스 보상으로 Z축을 따라 측정된 가속입니다.
TYPE_GRAVITY SensorEvent.values[0] X축을 따른 중력입니다. m/s2
SensorEvent.values[1] y축을 따라 작용하는 중력입니다.
SensorEvent.values[2] z축을 따른 중력입니다.
TYPE_GYROSCOPE SensorEvent.values[0] X축 기준의 회전 속도입니다. rad/s
SensorEvent.values[1] y축 기준의 회전 속도입니다.
SensorEvent.values[2] z축 기준의 회전 속도입니다.
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] x축 기준의 회전 속도 (드리프트 보상 없음)입니다. rad/s
SensorEvent.values[1] y축 기준의 회전 속도 (드리프트 보상 없음)입니다.
SensorEvent.values[2] z축 기준의 회전 속도 (드리프트 보상 없음)입니다.
SensorEvent.values[3] X축 기준으로 추정된 드리프트입니다.
SensorEvent.values[4] y축을 기준으로 추정된 드리프트입니다.
SensorEvent.values[5] Z축 기준으로 추정된 드리프트입니다.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] x축을 따른 가속력 (중력 제외)입니다. m/s2
SensorEvent.values[1] y축을 따른 가속력 (중력 제외)입니다.
SensorEvent.values[2] z축을 따른 가속력 (중력 제외)입니다.
TYPE_ROTATION_VECTOR SensorEvent.values[0] x축을 따라 회전하는 벡터 구성요소 (x * sin(θ/2))입니다. 단위 없음
SensorEvent.values[1] y축을 따르는 회전 벡터 구성요소 (y * sin(θ/2))입니다.
SensorEvent.values[2] z축을 따르는 회전 벡터 구성요소 (z * sin(θ/2))입니다.
SensorEvent.values[3] 회전 벡터의 스칼라 구성요소 ((cos(θ/2))1
TYPE_SIGNIFICANT_MOTION 해당 사항 없음 해당 사항 없음 해당 사항 없음
TYPE_STEP_COUNTER SensorEvent.values[0] 센서가 활성화된 상태에서 마지막 재부팅 이후에 사용자가 걸은 걸음수입니다. 단계
TYPE_STEP_DETECTOR 해당 사항 없음 해당 사항 없음 해당 사항 없음

1 스칼라 구성요소는 선택적 값입니다.

회전 벡터 센서와 중력 센서는 움직임 감지 및 모니터링에 가장 자주 사용되는 센서입니다. 회전 벡터 센서는 특히 다용도로 사용할 수 있으며 동작 감지, 각도 변화 모니터링, 상대 방향 변화 모니터링과 같은 다양한 동작 관련 작업에 사용할 수 있습니다. 예를 들어 회전 벡터 센서는 게임, 증강 현실 애플리케이션, 2차원 또는 3차원 나침반, 카메라 안정화 앱을 개발하는 경우에 적합합니다. 대부분의 경우 이러한 센서를 사용하는 것이 가속도계 및 지자기장 센서 또는 방향 센서를 사용하는 것보다 더 나은 선택입니다.

Android 오픈소스 프로젝트 센서

Android 오픈소스 프로젝트 (AOSP)는 중력 센서, 선형 가속 센서, 회전 벡터 센서라는 세 가지 소프트웨어 기반 움직임 감지 센서를 제공합니다. 이러한 센서는 Android 4.0에서 업데이트되었으며 이제 기기의 자이로스코프를 사용하여 (다른 센서 외에도) 안정성과 성능을 개선합니다. 이러한 센서를 사용해 보려면 getVendor() 메서드와 getVersion() 메서드를 사용하여 센서를 식별하면 됩니다(공급업체는 Google LLC, 버전 번호는 3). Android 시스템은 이 세 가지 센서를 보조 센서로 간주하므로 공급업체 및 버전 번호로 이러한 센서를 식별해야 합니다. 예를 들어 기기 제조업체가 자체 중력 센서를 제공하는 경우 AOSP 중력 센서는 보조 중력 센서로 표시됩니다. 이 세 가지 센서는 모두 자이로스코프를 사용합니다. 기기에 자이로스코프가 없으면 이러한 센서가 표시되지 않으며 사용할 수 없습니다.

중력 센서 사용

중력 센서는 중력의 방향과 크기를 나타내는 3차원 벡터를 제공합니다. 일반적으로 이 센서는 공간에서 기기의 상대적 방향을 확인하는 데 사용됩니다. 다음 코드는 기본 중력 센서의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

단위는 가속 센서에서 사용하는 단위 (m/s2)와 동일하며 좌표계는 가속 센서에서 사용하는 좌표계와 동일합니다.

참고: 기기가 대기 상태일 때는 중력 센서의 출력이 가속도계의 출력과 동일해야 합니다.

선형 가속도계 사용

선형 가속 센서는 중력을 제외한 각 기기 축을 따라 가속을 나타내는 3차원 벡터를 제공합니다. 이 값을 사용하여 동작 감지를 실행할 수 있습니다. 이 값은 추측 항법을 사용하는 관성 항법 시스템의 입력으로도 사용할 수 있습니다. 다음 코드는 기본 선형 가속 센서의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

개념적으로 이 센서는 다음 관계에 따라 가속 데이터를 제공합니다.

linear acceleration = acceleration - acceleration due to gravity

일반적으로 중력의 영향 없이 가속도 데이터를 가져오려는 경우 이 센서를 사용합니다. 예를 들어 이 센서를 사용하여 자동차의 속도를 확인할 수 있습니다. 선형 가속 센서에는 항상 오프셋이 있으므로 이를 삭제해야 합니다. 가장 간단한 방법은 애플리케이션에 보정 단계를 빌드하는 것입니다. 보정 중에 사용자에게 기기를 테이블에 설정하도록 요청한 다음 세 축 모두의 오프셋을 읽을 수 있습니다. 그런 다음 가속 센서의 직접 판독값에서 이 오프셋을 빼서 실제 선형 가속을 얻을 수 있습니다.

센서 좌표계는 가속 센서에서 사용하는 것과 동일하며 측정 단위(m/s2)도 동일합니다.

회전 벡터 센서 사용

회전 벡터는 기기의 방향을 각도와 축의 조합으로 나타냅니다. 여기서 기기는 축 (x, y 또는 z)을 기준으로 각도 θ만큼 회전합니다. 다음 코드는 기본 회전 벡터 센서의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

회전 벡터의 세 가지 요소는 다음과 같이 표현됩니다.

x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)

여기서 회전 벡터의 크기는 sin(θ/2)와 같고 회전 벡터의 방향은 회전축의 방향과 같습니다.

그림 1. 회전 벡터 센서에서 사용하는 좌표계

회전 벡터의 세 가지 요소는 단위 쿼터니언 (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2))의 마지막 세 가지 구성요소와 같습니다. 회전 벡터의 요소는 단위가 없습니다. x, y, z축은 가속 센서와 동일한 방식으로 정의됩니다. 참조 좌표계는 직접적인 정규직교 기반으로 정의됩니다 (그림 1 참고). 이 좌표계에는 다음과 같은 특징이 있습니다.

  • X는 벡터곱 Y x Z로 정의됩니다. 기기의 현재 위치에서 지면과 접하며 대략 동쪽을 가리킵니다.
  • Y는 기기의 현재 위치에서 지면과 관계가 없으며 지자기 북극을 가리킵니다.
  • Z는 하늘을 가리키며 지면과 수직을 이룹니다.

회전 벡터 센서를 사용하는 방법을 보여주는 샘플 애플리케이션은 RotationVectorDemo.java를 참고하세요.

큰 움직임 감지 센서 사용

중요한 움직임 센서는 중요한 움직임이 감지될 때마다 이벤트를 트리거한 다음 자체적으로 사용 중지됩니다. 중요한 움직임은 사용자 위치의 변화로 이어질 수 있는 움직임입니다(예: 걷기, 자전거 타기, 움직이는 차에 앉아 있기). 다음 코드는 기본 주요 움직임 감지 센서의 인스턴스를 가져오는 방법과 이벤트 리스너를 등록하는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
val triggerEventListener = object : TriggerEventListener() {
    override fun onTrigger(event: TriggerEvent?) {
        // Do work
    }
}
mSensor?.also { sensor ->
    sensorManager.requestTriggerSensor(triggerEventListener, sensor)
}

자바

private SensorManager sensorManager;
private Sensor sensor;
private TriggerEventListener triggerEventListener;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

triggerEventListener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // Do work
    }
};

sensorManager.requestTriggerSensor(triggerEventListener, mSensor);

자세한 내용은 TriggerEventListener를 참조하세요.

만보기 센서 사용

걸음수 측정기 센서는 센서가 활성화된 상태에서 마지막 재부팅 이후에 사용자가 걸은 걸음수를 제공합니다. 걸음수 측정기는 걸음 감지기 센서보다 지연 시간이 길지만 (최대 10초) 더 정확합니다.

참고: Android 10 (API 수준 29) 이상을 실행하는 기기에서 앱이 이 센서를 사용하려면 ACTIVITY_RECOGNITION 권한을 선언해야 합니다.

다음 코드는 기본 걸음 수 카운터 센서의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

앱을 실행하는 기기의 배터리를 보존하려면 JobScheduler 클래스를 사용하여 특정 간격으로 걸음 수 카운터 센서에서 현재 값을 가져와야 합니다. 앱 유형에 따라 센서 판독 간격이 다르지만 앱에서 센서의 실시간 데이터를 요구하지 않는 한 이 간격을 최대한 길게 설정해야 합니다.

보행 감지기 센서 사용

걸음 감지기 센서는 사용자가 한 걸음을 걸을 때마다 이벤트를 트리거합니다. 지연 시간은 2초 미만으로 예상됩니다.

참고: Android 10 (API 수준 29) 이상을 실행하는 기기에서 앱이 이 센서를 사용하려면 ACTIVITY_RECOGNITION 권한을 선언해야 합니다.

다음 코드는 기본 걸음 감지기 센서의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

원시 데이터 작업

다음 센서는 기기에 적용되는 선형 및 회전력에 관한 원시 데이터를 앱에 제공합니다. 이러한 센서의 값을 효과적으로 사용하려면 중력과 같은 환경 요소를 필터링해야 합니다. 또한 노이즈를 줄이기 위해 값 추세에 평활화 알고리즘을 적용해야 할 수도 있습니다.

가속도계 사용

가속 센서는 중력을 포함하여 기기에 적용되는 가속을 측정합니다. 다음 코드는 기본 가속 센서의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

자바

private SensorManager sensorManager;
private Sensor sensor;
  ...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

참고: 앱이 Android 12 (API 수준 31) 이상을 타겟팅하는 경우 이 센서는 비율 제한이 적용됩니다.

개념적으로 가속도 센서는 다음 관계를 사용하여 센서 자체에 적용되는 힘 (Fs)을 측정하여 기기에 적용되는 가속도 (Ad)를 결정합니다.

A_D=-(1/mass)∑F_S

하지만 중력은 항상 다음 관계에 따라 측정된 가속도에 영향을 미칩니다.

A_D=-g-(1/mass)∑F_S

따라서 기기가 테이블에 놓여 있고 가속되지 않는 경우 가속도계는 g = 9.81m/s2의 크기를 읽습니다. 마찬가지로 기기가 자유 낙하 중이어서 지면을 향해 9.81m/s2로 빠르게 가속하는 경우 가속도계는 g = 0m/s2의 크기를 읽습니다. 따라서 기기의 실제 가속도를 측정하려면 가속도계 데이터에서 중력의 기여도를 삭제해야 합니다. 이는 고역 통과 필터를 적용하여 달성할 수 있습니다. 반대로 저역 통과 필터를 사용하여 중력을 분리할 수 있습니다. 다음 예시에서는 이 작업을 수행하는 방법을 보여줍니다.

Kotlin

override fun onSensorChanged(event: SensorEvent) {
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    val alpha: Float = 0.8f

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0]
    linear_acceleration[1] = event.values[1] - gravity[1]
    linear_acceleration[2] = event.values[2] - gravity[2]
}

자바

public void onSensorChanged(SensorEvent event){
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    final float alpha = 0.8;

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0];
    linear_acceleration[1] = event.values[1] - gravity[1];
    linear_acceleration[2] = event.values[2] - gravity[2];
}

참고: 다양한 기법을 사용하여 센서 데이터를 필터링할 수 있습니다. 위의 코드 샘플은 간단한 필터 상수 (알파)를 사용하여 로우 패스 필터를 만듭니다. 이 필터 상수는 필터가 센서 이벤트에 추가하는 지연 시간을 대략적으로 나타내는 시간 상수 (t)와 센서의 이벤트 전송 속도 (dt)에서 파생됩니다. 코드 샘플에서는 데모 목적으로 알파 값 0.8을 사용합니다. 이 필터링 방법을 사용하는 경우 다른 알파 값을 선택해야 할 수 있습니다.

가속도계는 표준 센서 좌표계를 사용합니다. 실제로 이는 기기가 자연스러운 방향으로 테이블에 평평하게 놓여 있을 때 다음 조건이 적용됨을 의미합니다.

  • 기기를 왼쪽에서 밀면 (오른쪽으로 이동) x 가속 값은 양수입니다.
  • 기기를 하단에서 밀면 (사용자로부터 멀어짐) y 가속 값은 양수입니다.
  • A m/s2의 가속도로 기기를 하늘 쪽으로 밀면 z 가속 값은 A + 9.81과 같으며, 이는 기기 가속 (+A m/s2)에서 중력 (-9.81 m/s2)을 뺀 값에 해당합니다.
  • 정지된 기기의 가속 값은 +9.81이며, 이는 기기 가속(0m/s2)에서 중력(-9.81m/s2)을 뺀 값에 해당합니다.

일반적으로 가속도계는 기기 움직임을 모니터링하는 데 사용하기 좋은 센서입니다. 거의 모든 Android 기반 핸드셋과 태블릿에는 가속도계가 있으며, 다른 움직임 감지 센서보다 약 10배 적은 전력을 사용합니다. 한 가지 단점은 중력을 제거하고 노이즈를 줄이기 위해 로우패스 및 하이패스 필터를 구현해야 할 수 있다는 것입니다.

자이로스코프 사용

자이로스코프는 기기의 x, y, z축을 기준으로 회전 속도(rad/s)를 측정합니다. 다음 코드는 기본 자이로스코프의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

참고: 앱이 Android 12 (API 수준 31) 이상을 타겟팅하는 경우 이 센서는 비율 제한이 적용됩니다.

센서의 좌표계는 가속 센서에 사용되는 좌표계와 동일합니다. 회전은 시계 반대 방향으로 양수입니다. 즉, 기기가 원점에 위치한 상태에서 X, Y 또는 Z축의 양수 위치에서 기기를 관찰하는 관찰자가 기기가 시계 반대 방향으로 회전하는 것처럼 보일 경우 양수 회전을 보고하게 됩니다. 이는 양수 회전의 표준적인 수학적 정의이며 방향 센서에서 사용하는 롤(roll)에 관한 정의와 일치하지 않습니다.

일반적으로 자이로스코프의 출력은 시간 경과에 따라 통합되어 시간 단계에 따른 각도 변화를 설명하는 회전을 계산합니다. 예를 들면 다음과 같습니다.

Kotlin

// Create a constant to convert nanoseconds to seconds.
private val NS2S = 1.0f / 1000000000.0f
private val deltaRotationVector = FloatArray(4) { 0f }
private var timestamp: Float = 0f

override fun onSensorChanged(event: SensorEvent?) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0f && event != null) {
        val dT = (event.timestamp - timestamp) * NS2S
        // Axis of the rotation sample, not normalized yet.
        var axisX: Float = event.values[0]
        var axisY: Float = event.values[1]
        var axisZ: Float = event.values[2]

        // Calculate the angular speed of the sample
        val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)

        // Normalize the rotation vector if it's big enough to get the axis
        // (that is, EPSILON should represent your maximum allowable margin of error)
        if (omegaMagnitude > EPSILON) {
            axisX /= omegaMagnitude
            axisY /= omegaMagnitude
            axisZ /= omegaMagnitude
        }

        // Integrate around this axis with the angular speed by the timestep
        // in order to get a delta rotation from this sample over the timestep
        // We will convert this axis-angle representation of the delta rotation
        // into a quaternion before turning it into the rotation matrix.
        val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
        val sinThetaOverTwo: Float = sin(thetaOverTwo)
        val cosThetaOverTwo: Float = cos(thetaOverTwo)
        deltaRotationVector[0] = sinThetaOverTwo * axisX
        deltaRotationVector[1] = sinThetaOverTwo * axisY
        deltaRotationVector[2] = sinThetaOverTwo * axisZ
        deltaRotationVector[3] = cosThetaOverTwo
    }
    timestamp = event?.timestamp?.toFloat() ?: 0f
    val deltaRotationMatrix = FloatArray(9) { 0f }
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

자바

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0) {
      final float dT = (event.timestamp - timestamp) * NS2S;
      // Axis of the rotation sample, not normalized yet.
      float axisX = event.values[0];
      float axisY = event.values[1];
      float axisZ = event.values[2];

      // Calculate the angular speed of the sample
      float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

      // Normalize the rotation vector if it's big enough to get the axis
      // (that is, EPSILON should represent your maximum allowable margin of error)
      if (omegaMagnitude > EPSILON) {
        axisX /= omegaMagnitude;
        axisY /= omegaMagnitude;
        axisZ /= omegaMagnitude;
      }

      // Integrate around this axis with the angular speed by the timestep
      // in order to get a delta rotation from this sample over the timestep
      // We will convert this axis-angle representation of the delta rotation
      // into a quaternion before turning it into the rotation matrix.
      float thetaOverTwo = omegaMagnitude * dT / 2.0f;
      float sinThetaOverTwo = sin(thetaOverTwo);
      float cosThetaOverTwo = cos(thetaOverTwo);
      deltaRotationVector[0] = sinThetaOverTwo * axisX;
      deltaRotationVector[1] = sinThetaOverTwo * axisY;
      deltaRotationVector[2] = sinThetaOverTwo * axisZ;
      deltaRotationVector[3] = cosThetaOverTwo;
    }
    timestamp = event.timestamp;
    float[] deltaRotationMatrix = new float[9];
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

표준 자이로스코프는 노이즈 및 드리프트 (바이어스)에 대한 필터링이나 수정 없이 원시 회전 데이터를 제공합니다. 실제로 자이로스코프 노이즈와 드리프트는 보정해야 하는 오류를 발생시킵니다. 일반적으로 중력 센서나 가속도계와 같은 다른 센서를 모니터링하여 드리프트 (바이어스)와 노이즈를 확인합니다.

미보정 자이로스코프 사용

미보정 자이로스코프는 자이로스코프와 유사하지만 회전 속도에 자이로 드리프트 보정이 적용되지 않습니다. 공장 보정 및 온도 보상은 회전율에 계속 적용됩니다. 보정되지 않은 자이로스코프는 방향 데이터의 후처리 및 병합에 유용합니다. 일반적으로 gyroscope_event.values[0]는 uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]에 가깝습니다. 즉, 다음과 같습니다.

calibrated_x ~= uncalibrated_x - bias_estimate_x

참고: 미보정 센서는 좀 더 원시적인 결과를 제공하며 일부 바이어스를 포함할 수 있습니다. 또한 보정을 통해 적용된 교정값의 점프를 훨씬 적게 포함할 수도 있습니다. 일부 애플리케이션은 좀 더 매끄럽고 안정적인 미보정 결과를 선호할 수 있습니다. 예를 들어 애플리케이션에서 센서 융합을 시도할 경우, 보정을 적용하면 실제로는 결과가 왜곡될 수 있습니다.

미보정 자이로스코프는 회전 속도 외에도 각 축의 예상 드리프트를 제공합니다. 다음 코드는 기본 보정되지 않은 자이로스코프의 인스턴스를 가져오는 방법을 보여줍니다.

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)

자바

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

추가 코드 샘플

BatchStepSensor 샘플은 이 페이지에 설명된 API를 사용하는 방법을 자세히 보여줍니다.

참고 사항