가속도 센서로 속도 계산, 차량 견인 감지 하기

가속도 센서로 속도도 알 수 있을까? 속도의 변화가 가속도니까  당연히 될 것 같지만 막상 해보면 쉽지 않다. 여기에는 중력이라는 아주 큰 함정이 숨어있기 때문이다. 이 글에서는 가속도 센서 데이터에서 중력을 분리해내고, 순수한 움직임에 의한 속도를 계산하는 원리와 코드를 살펴 보기로 한다.

이 글을 읽기 전에 가속도 센서의 기본 원리나 Pitch, Roll 같은 용어가 생소하다면, 아래 글을 먼저 읽어보는 것을 추천한다.

속도 계산의 핵심인 선형 가속도 구하기

가속도 센서가 측정하는 값은 중력 가속도와 실제 물체의 움직임으로 인한 운동 가속도가 합쳐진 값이다. 우리가 원하는 것은 순수한 운동 가속도인 선형 가속도(Linear Acceleration)다. 따라서 이 합쳐진 값에서 중력 가속도를 정확히 분리해내는 과정이 필수적이다.

  1. 현재 기울기 파악: 가속도 센서의 값으로 현재 물체의 Pitch, Roll 기울기 계산
  2. 중력 벡터 계산: 현재 기울어진 상태에서 중력(1g, 약 9.8m/s²)이 각 X, Y, Z축에 얼마나 투영되어 나타나는지 계산 함.
  3. 중력 제거: 센서가 측정한 전체 가속도 값에서 위에서 계산한 중력 벡터 성분을 빼준다.

이 과정을 거치면 비로소 물체의 순수한 운동 가속도, 즉 선형 가속도를 얻을 수 있다.

속도 계산 공식과 소스코드

순수한 선형 가속도를 얻었다면, 시간에 대해 적분하여 속도를 추정할 수 있다고 한다.

Linear Accel=Measured Accel−Gravity Vector
Velocity_new=Velocity_old+LinearAccel×Δt


AI의 도움으로 소스코드를 작성하였다.

// 속도를 저장할 구조체 (예시)
typedef struct {
    float x, y, z;
} Velocity;

// 가속도(g)를 m/s^2 단위로 변환하는 상수 (1g = 9.8m/s^2)
#define GRAVITY_MSS 9.80665f

/**
 * @brief 중력을 제거한 선형 가속도를 계산하고, 이를 적분하여 속도를 추정한다.
 * @param imu_data 현재 센서 데이터 (가속도는 g 단위)
 * @param current_angles 현재 기울기 정보 (Pitch, Roll은 degree 단위)
 * @param velocity 이전 속도 값이자, 새로운 속도를 저장할 포인터 (m/s 단위)
 * @param dt 시간 변화량 (초)
 */
void update_velocity(const ImuData* imu_data, const Angles* current_angles, Velocity* velocity, float dt) {
    // 1. 현재 기울기를 이용해 중력 벡터 성분을 계산한다 (센서 좌표계 기준).
    // 실제 구현에서는 더 정확한 회전 변환을 사용해야 한다.
    float roll_rad = current_angles->roll * (M_PI / 180.0f);
    float pitch_rad = current_angles->pitch * (M_PI / 180.0f);
    float gravity_x_component = sin(pitch_rad);
    float gravity_y_component = -cos(pitch_rad) * sin(roll_rad);
    
    // 2. 센서 측정값에서 중력 성분을 뺀다 (선형 가속도).
    float linear_accel_x_g = imu_data->accel_x - gravity_x_component;
    float linear_accel_y_g = imu_data->accel_y - gravity_y_component;
    
    // 3. 가속도(g)를 m/s^2 단위로 변환한다.
    float linear_accel_x_mss = linear_accel_x_g * GRAVITY_MSS;
    float linear_accel_y_mss = linear_accel_y_g * GRAVITY_MSS;

    // 4. 속도 계산 (v = v0 + a*t)
    velocity->x += linear_accel_x_mss * dt;
    velocity->y += linear_accel_y_mss * dt;
}

그런데 이 방법은 오차 누적이 매우 심하다. 센서의 미세한 노이즈와 바이어스, 그리고 중력 제거의 불완전함 때문에 가만히 냅둬도 속도가 계속 올라가는 ‘드리프트(Drift)’ 현상을 보게 될 것이다.

아무리 센서 값을 최신으로 읽고 정교하게 계산해도, 적분이라는 수학적 과정의 특성상 미세한 오차가 지속적으로 누적되기 때문에 드리프트를 피할 수 없다.


정밀한 속도가 아닌 대략적인 속도

위와 같은 이유로 센서값으로 정밀한 속도값을 계산하는 것은 안타깝게도 불가능에 가깝니다. 하지만 모든 경우에 정밀한 속도가 필요한 것은 아니다.

  • 대략적인 속도 변화 감지: 물체가 움직이기 시작했는지, 멈췄는지, 갑작스러운 충격이 있었는지 등 순간적인 가속도 값이나 단시간 동안의 가속도 변화를 파악하는 용도로는 가속도 센서가 매우 효과적이다. 활동량 측정, 충격 감지, 차량의 움직임 감지 등에서 유용하게 활용될 수 있다.
  • 대략적인 이동 거리 추정: 아주 짧은 시간 동안 이동한 대략적인 거리를 추정하는 데는 제한적으로 활용될 수 있다. 하지만 누적 오차가 심하므로, 장시간, 넓은 공간에서 정확한 속도나 위치를 추정하는 것은 GPS 같은 외부 센서 없이는 거의 불가능하다.

따라서 정밀한 절대 속도가 아닌 대략적인 속도 변화를 감지하는 것이 목적이라면, 가속도 센서는 매우 유용하고 비용 효율적인 솔루션이 될 수 있다.


시나리오 적용: 주차된 차가 견인될 때 감지하기

이 모든 기술을 종합해 실용적인 시나리오를 만들어보자. 우리가 만들 시나리오는 시동이 꺼진 차가 움직일때 알림을 보내는 견인 감지 시나리오다.

먼저 차가 견인되는 상황을 생각해 보자. 그것은 크게 두 가지로, 기울기와 선형 가속도 개념으로 감지할 수 있다.

  1. 견인차에 들려 올라갈 때: 차의 기울기(Pitch/Roll)가 크게 변한다.
  2. 앞에서 끌려갈 때: 차에 직선 운동(선형 가속도)이 발생한다.
차가 들려 올라갈때, 기울기가 변한다


이 두 가지를 감지하는 코드를 만들어 볼 수 있다.

#define TILT_THRESHOLD_DEG 5.0      // 5도 이상 기울어지면 감지
#define ACCEL_THRESHOLD_G 0.1       // 0.1g 이상의 순수 선형 가속도가 감지되면 (수평 이동)

/**
 * @brief 차량 견인 상황을 감지하는 함수
 * @param imu_data 현재 센서 데이터
 * @param is_ignition_on 차량 시동 상태 (true: ON, false: OFF)
 * @return 견인 의심 상황이면 true 반환
 */
bool check_towing_scenario(const ImuData* imu_data, bool is_ignition_on) {
    // 시동이 켜져 있으면 정상 주행이므로 감지 안 함
    if (is_ignition_on) {
        return false;
    }

    // 1. 기울기 변화 감지 (견인차에 들릴 때)
    Angles current_tilt = {0};
    // calculate_tilt 함수는 "가속도계(Accelerometer)와 방향 계산" 글에서 정의된 함수
    calculate_tilt(imu_data, ¤t_tilt); 

    if (fabs(current_tilt.pitch) > TILT_THRESHOLD_DEG || fabs(current_tilt.roll) > TILT_THRESHOLD_DEG) {
        // 실제 제품에서는 '일정 시간 이상' 기울기가 유지되는지 확인하는 로직 추가
        return true; 
    }

    // 2. 선형 가속도 감지 (앞에서 끌려갈 때)
    // (간단한 예시로, Z축 가속도가 중력(1g)에서 크게 벗어나지 않으면 수평 이동으로 간주)
    float horizontal_accel_x = imu_data->accel_x; // 대략적인 수평 가속도
    float horizontal_accel_y = imu_data->accel_y; // 대략적인 수평 가속도
    float total_horizontal_accel_g = sqrt(horizontal_accel_x * horizontal_accel_x + horizontal_accel_y * horizontal_accel_y);

    if (total_horizontal_accel_g > ACCEL_THRESHOLD_G) {
        // 실제 제품에서는 '일정 시간 이상' 가속도가 유지되는지 확인하는 로직 추가
        return true;
    }

    return false;
}

이 코드는 센서 값과 차량의 시동 상태를 받아, 시동이 꺼진 상태에서 일정 값 이상의 기울기나 수평 움직임이 감지되면 견인 상황으로 판단한다. 물론 실제 제품에 적용하려면 노이즈 필터링, 이벤트 지속 시간 체크, 캘리브레이션 등 훨씬 정교한 로직이 필요하다.

마무리하며: 센서 데이터, 이해하면 길이 보인다

가속도 센서로 속도를 계산하는 과정의 핵심은 중력 제거에 있다는 것을 확인했다. 이 원리를 응용하면 차량의 견인 감지와 같이 실생활의 특정 문제를 해결하는 아이디어를 구현해 볼 수 있다. 물론 센서의 노이즈와 오차 누적이라는 한계도 명확하지만, 그 특성을 정확히 이해하고 활용하는 것이 중요하다.

센서 데이터는 처음 보면 그저 복잡한 숫자들의 나열처럼 보일 수 있다. 하지만 그 안에 담긴 물리적 원리를 이해하고 나면, 모든 데이터가 의미 있는 정보로 바뀌게 된다. 이 글에서 다룬 원리와 코드 예제가 더 복잡한 애플리케이션 개발로 나아가는 튼튼한 첫걸음이 되기를 바란다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다