본문 바로가기
임베디드 개발/펌웨어

선형 보간 (Linear Interpolation)

by eteo 2025. 12. 25.
반응형

 

 

 

선형 보간 (Linear Interpolation)

먼저 보간(interpolation)은 알려진 데이터 점들 사이의 값을 추정하는 수학적 기법을 말한다.

 

그 중에서도 선형 보간(Linear Interpolation)은 각 구간 내에서 출력이 입력에 대해 선형(직선) 관계를 따른다고 가정하는 가장 기본적인 보간 방법이다.

 

참고로 선형 보간은 차원에 따라 다음과 같이 구분될 수 있다. 다만 2차원, 3차원부터는 용어가 구체화되기 때문에 일반적으로 선형 보간이라고만 하면 1차원을 의미하는 경우가 많다.

  • (1D) Linear Interpolation : 직선 상에서 두 개의 기준점을 이용해 값을 추정하는 방식, y = f(x)
  • (2D) Bilinear Interpolation : 좌표 평면 상에서 네 개의 기준점을 이용해 값을 추정하는 방식, z = f(x, y)
  • (3D) Trilinear Interpolation : 공간 상에서 여덟 개의 기준점을 이용해 값을 추정하는 방식, w = f(x, y, z)

 

https://pimiddy.wordpress.com/2011/01/20/n-dimensional-interpolation/

 

 

다시 선형 보간에 대해 더 구체적으로 설명하면 이미 알고 있는 두 데이터 포인트 (x0, y0), (x1, y1) 가 있을 때 x0와 x1 사이에 존재하는 임의의 입력 값 x에 대한 출력 값 y를 두 점을 잇는 직선 위의 값이라고 추정하는 방법이다.

 

 

 

 

 

 

 

 

공식

선형보간의 공식은 두 점을 잇는 직선의 방정식과 동일하다.



 

 

 

 

 

사용 목적

  1. 데이터 평활화 (Smoothing) : 센서의 노이즈가 심할 때 보간을 통해 급격한 변화를 줄여 제어의 안정성을 높일 수 있다.
  2. 데이터 샘플 수 증가 (Up-Sampling) : 데이터 포인트가 부족할 때 이미 존재하는 샘플들 사이에 보간을 통해 가상의 중간 데이터를 생성해서 신호의 해상도를 높일 수 있다.
  3. 보정 (Calibration) : 센서 출력이 비선형일 때 미리 측정한 기준점을 사용해 구간을 정의하고, 각 구간 내의 사이 값은 보간을 통해 실제 물리량으로 변환할 수 있다.

 아래에서는 3번째 사용 사례를 기준으로 조금 더 자세히 설명하고자 한다.

 

 

 

 

 

 

 

 

 

사용 사례

 

센서 출력이 연속적인 물리량인데, 데이터시트나 실험 데이터는 이산 포인트만 제공된다고 해보자. 이 때 중간 구간의 값은 정확히 정의되어 있지 않으므로 합리적으로 추정하는 과정이 필요하다.

 

만약 전체 구간에서 입력-출력 관계가 완전히 리니어하다면, 그냥 기울기랑 오프셋 구해서 하나의 수식으로 표현할 수 있다.

 

y = ax + b

 

하지만 현실에서 센서 출력은 구간에 따라 기울기가 달라지는 비선형 특성을 보이는 경우가 많다.

 

이 때는 단일 공식으로 전체 특성을 표현하기 어렵기 때문에 미리 측정한 기준점을 LUT(Look-Up Table) 형태로 저장해 두고, 입력된 센서 값이 어느 구간에 속하는지 먼저 탐색한 뒤 해당 구간에 대해 선형보간을 수행하여 출력 값을 계산하는 방식을 사용할 수 있다.

 

 

예를 들어 RPM 센서가 있는데 다음과 같이 낮은 RPM 영역에서는 출력 전압이 둔감하게 변화하고, 중간 RPM 영역에서는 전압 민감도가 증가하고, 높은 RPM 영역에서는 출력 전압이 포화되어 다시 둔감해지는 비선형 특성을 가진다고 해보자.

 

가지고 있는 실측 데이터는 다음이 전부이다.

 

센서 출력 (mV) RPM
0 0
500 900
1000 2200
1500 3700
2000 5300
2500 7500
3000 9000
3300 9600

 



 

 

이 때, 아래와 같이 LUT를 정의해 두면, 기준점에 직접 대응되지 않는 입력 값에 대해서도 구간 탐색과 선형보간을 통해 출력 값을 산출할 수 있다.

#include <stdio.h>
#include <stdint.h>

typedef struct {
    int32_t x;  // 입력값
    int32_t y;  // 출력값
} rpm_lut_t;

static const rpm_lut_t rpm_lut[] = {
    {    0, 0    },
    {  500, 900  },
    { 1000, 2200 },
    { 1500, 3700 },  
    { 2000, 5300 },
    { 2500, 7500 },
    { 3000, 9000 },
    { 3300, 9600 },
};

#define RPM_LUT_SIZE (sizeof(rpm_lut) / sizeof(rpm_lut[0]))

int32_t rpm_sensor_convert(int32_t input) {
    int32_t output = 0;
    
    // 범위 클램프
    if(input <= rpm_lut[0].x) {
        output = rpm_lut[0].y;
    }
    else if(input >= rpm_lut[RPM_LUT_SIZE - 1].x) {
        output = rpm_lut[RPM_LUT_SIZE - 1].y;
    }
    // 구간 탐색
    else {
        for(int i = 0; i < RPM_LUT_SIZE - 1; i++) {
            int32_t x0 = rpm_lut[i].x;
            int32_t x1 = rpm_lut[i + 1].x;
            if(input >= x0 && input <= x1) {
                int32_t y0 = rpm_lut[i].y;
                int32_t y1 = rpm_lut[i + 1].y;
                // 곱셈에서 오버플로우 방지용 int64_t 캐스팅
                output = (int32_t)(y0 + (int64_t)(input - x0) * (y1 -y0) / (x1 - x0));
                break;
            }
        }
    }
    
    return output;
}

int main()
{
    int32_t input = 2376;
    
    printf("Sensor value: %dmV, RPM: %d\n", input, rpm_sensor_convert(input));

    return 0;
}

 

 

 

 

반응형