본문 바로가기
임베디드 기초

히스테리시스(Hysteresis) 완벽 정리: 불안정한 경계값을 안정화시키는 기법

by 버그없는토마토 2026. 1. 8.

히스테리시스(Hysteresis) 완벽 정리: 불안정한 경계값을 안정화시키는 기법

히스테리시스(Hysteresis)란?


들어가며

자동차 ECU는 계속해서 경계값 판정을 합니다.

엔진 온도 95°C 이상이면 "과열"? 배터리 전압 12V 이하면 "저전압"? 연료 게이지가 25% 이하면 "경고등"?

이렇게 '단순한' 임계값(Threshold) 설정은 실무에서는 재앙이 되게 됩니다.

왜냐하면 센서 신호는 경계값 근처에서 미세하게 진동하거든요.

온도가 94.9°C ↔ 95.1°C를 오가며 진동하면, 당연히 상태창에선 "정상"과 "과열"이 계속 바뀌겠죠.

그럼 결론적으로 과열 경고등이 계속해서 깜빡일 거예요.

그럼 엔진 제어가 불안정해지게 되고 앞서 말했듯 재앙이 됩니다.

이게 바로 이전 편에 얘기했던 채터링의 문제와 이어집니다.

하지만 히스테리시스(Hysteresis)를 사용하면 이 모든 문제가 해결되게 되죠.

경계값을 두 개로 분리하는 거예요.

"올라갈 때는 95°C, 내려갈 때는 93°C".

이렇게 하면 경계값 근처의 진동은 무시되고, 신호는 안정적이 됩니다.

이 글에서는 히스테리시스의 원리, 자동차 시스템에서의 적용, 그리고 실무 구현법을 정리한다.


1. 히스테리시스(Hysteresis)란?

1.1 정의

히스테리시스 = 상태 변화가 경로에 따라 달라지는 현상

더 정확하게:

히스테리시스 = 입력값이 같아도 이전 상태에 따라 출력이 달라지는 현상

일반적인 임계값 (Threshold):

입력값
│
95 ────────────────────── Threshold
│     ❌ 경계값 근처
93 ────────────────────── 에서 불안정
│
90 ────────────────────────────────
└──────────────────────────────────
  0  5  10 15 20 25 30 35 40 45 (ms)

입력:  94 94.5 95.1 95 94.9 95.2 94.8 (±진동)

출력:  0   0    1   1   0    1    0   (계속 바뀜! ❌)


히스테리시스 (2개의 경계값):

입력값
│
95 ────────── Upper Threshold ── (올라갈 때)
│
93 ────────── Lower Threshold ── (내려갈 때)
│
90 ────────────────────────────
└──────────────────────────────────
  0  5  10 15 20 25 30 35 40 45 (ms)

입력:  94 94.5 95.1 95 94.9 95.2 94.8 (±진동)

상태:  0   0    1   1   1    1    1   (안정화! ✓)

내려가려면 93°C 이하여야 함.

1.2 히스테리시스의 의미

이전 상태가 현재 판정에 영향을 미친다.

상태도 (State Diagram):

    95°C 초과
 ┌─────────────────────┐
 │                     ▼
[정상]            [과열]
 ▲                 │
 │                 │
 └─────────────────┘
   93°C 이하

특징:
- 정상 → 과열: 95°C 초과 필요
- 과열 → 정상: 93°C 이하 필요
- 94°C는 이전 상태를 유지

1.3 자동차에서의 예시

시스템 올라갈 때 내려갈 때 간격 목적
엔진 과열 95°C 90°C 5°C 과열 판정 안정화
저전압 경고 11.5V 12.5V 1V 배터리 전압 변동 대응
연료 부족 25% 35% 10% 연료 게이지 깜빡임 방지
팬 제어 100°C 95°C 5°C 냉각 팬 ON/OFF 진동 방지
기어 시프트 5000rpm 4500rpm 500rpm 변속기 주행 안정화

2. 히스테리시스가 필요한 이유

2.1 경계값 근처의 진동 문제

상황: 온도 센서가 95°C 경계값 근처에서 진동

센서 신호 (노이즈 포함):
시간    온도      상태 (95°C 기준)
───────────────────────────────
0ms    94.5°C    정상 (0)
5ms    94.8°C    정상 (0)
10ms   95.2°C    과열 (1) ← 진동 시작!
15ms   94.9°C    정상 (0)
20ms   95.3°C    과열 (1)
25ms   94.7°C    정상 (0)
30ms   95.1°C    과열 (1)
...

결과:
- 경고등이 깜빡깜빡
- 엔진 제어가 불안정
- 운전자가 불안감 느낌
- 시스템 신뢰성 저하


히스테리시스 적용 (Up: 95°C, Down: 90°C):

시간    온도      상태 (히스터시스)
───────────────────────────────
0ms    94.5°C    정상 (0)
5ms    94.8°C    정상 (0)
10ms   95.2°C    과열 (1) ← 95°C 초과하면 전환
15ms   94.9°C    과열 (1) ← 아직 90°C 이상이므로 유지
20ms   95.3°C    과열 (1)
25ms   94.7°C    과열 (1)
30ms   95.1°C    과열 (1)
...

결과:
- 경고등이 안정적
- 엔진 제어 안정화
- 운전자가 안심
- 시스템 신뢰성 향상 ✓

2.2 CPU 부하 감소

히스테리시스 없음:

매 샘플마다 경계값 판정
95°C 근처에서 100번 연산 가능

CPU 부하: 높음


히스테리시시 있음:

경계값을 벗어날 때까지 상태 유지
경계값 근처에서는 연산 생략

CPU 부하: 낮음

절감 효과:
- 불필요한 연산 60% 이상 감소
- 전력 소비 감소 (중요!)
- 실시간성 확보

3. 히스테리시스의 구현 원리

3.1 기본 로직

// 히스테리시스 없이 (불안정):
#define THRESHOLD 95.0f

uint8_t IsEngineOverheated_Simple(float temp) {
    if (temp > THRESHOLD) {
        return 1;  // 과열
    }
    return 0;  // 정상
}

// 문제: 94.9°C ↔ 95.1°C 진동 시 결과 계속 바뀜


// 히스테리시스 있음 (안정적):
#define TEMP_UPPER 95.0f  // 과열 전환 임계값
#define TEMP_LOWER 90.0f  // 정상 전환 임계값

static uint8_t engine_state = 0;  // 0: 정상, 1: 과열

uint8_t IsEngineOverheated_WithHysteresis(float temp) {
    if (engine_state == 0) {
        // 현재 정상 상태
        if (temp > TEMP_UPPER) {
            engine_state = 1;  // 과열로 전환
        }
    } else {
        // 현재 과열 상태
        if (temp < TEMP_LOWER) {
            engine_state = 0;  // 정상으로 전환
        }
    }

    return engine_state;
}

3.2 동작 흐름도

입력: 현재 온도값
    ▼
┌─────────────────────┐
│ 현재 상태 확인      │
└────────┬────────────┘
         │
     ┌───┴───┐
     │       │
  [정상]  [과열]
     │       │
     │   ┌───┴──────────────┐
     │   │ temp < 90°C?     │
     │   │ (Lower Threshold)│
     │   └──┬───────────┬───┘
     │      YES        NO
     │      │           │
     │   [정상] ← ┘    [과열]
     │              유지
     │
┌────┴───────────────┐
│ temp > 95°C?       │
│ (Upper Threshold)  │
└──┬────────────┬────┘
  YES          NO
  │            │
[과열]   [정상]
         유지

출력: 안정화된 상태

3.3 실제 동작 예시

// 온도 센서 읽기 (주기: 10ms)
void EngineControl_MonitorTemperature(void) {
    static uint8_t overheating = 0;

    float current_temp = TemperatureSensor_Read();

    // 히스테리시스 로직
    if (overheating == 0) {
        // 정상 상태 → 과열로 전환?
        if (current_temp > 95.0f) {
            overheating = 1;
            SetWarningLight(ENGINE_OVERHEAT_WARNING);
            ReduceEnginePower();
        }
    } else {
        // 과열 상태 → 정상으로 전환?
        if (current_temp < 90.0f) {
            overheating = 0;
            ClearWarningLight(ENGINE_OVERHEAT_WARNING);
            RestoreEnginePower();
        }
    }
}

4. 실무 적용 사례

4.1 Case 1: 엔진 온도 제어

설정:
- Upper (과열 시작): 95°C
- Lower (과열 해제): 90°C
- 간격(Hysteresis Band): 5°C

시나리오:

상황 1: 도시 주행 (온도 변동 크음)
80°C → 85°C → 89°C → 92°C → 94°C → 96°C ✓
                              ▲
                        95°C 초과 → 과열 전환

상황 2: 고속도로 (온도 안정적이지만 높음)
92°C → 93°C → 94°C → 94.9°C → 94.8°C → 94.9°C
                     (95°C 미달, 그대로 정상 유지) ✓

상황 3: 막힌 도로 (온도 올라감)
90°C → 92°C → 94°C → 95.5°C (과열) → 95.2°C (여전히 과열)
                → 93°C → 92°C → 89°C (90°C 미만 → 정상) ✓

효과:
✓ 경계값 근처의 진동 무시
✓ 경고등 깜빡임 방지
✓ 엔진 제어 안정화

4.2 Case 2: 배터리 저전압 경고

설정:
- Upper (저전압 시작): 11.5V
- Lower (저전압 해제): 12.5V
- 간격: 1V

시나리오:

상황 1: 엔진 시동 중 (전압 변동)
13.2V → 12.8V → 12.0V → 11.8V ✓ (11.5V 미달 → 저전압)
      → 12.2V → 12.0V → 11.9V (12.5V 미달, 저전압 유지)
      → 13.0V → 13.5V ✓ (12.5V 초과 → 정상)

결과:
- 11.8V에서 순간적으로 경고 나타남
- 12.2V로 올라와도 경고 유지
- 12.5V를 넘어야 경고 해제
- 경고등이 깜빡이지 않음 ✓

4.3 Case 3: CAN 신호 필터링

상황:
CAN에서 RPM 신호를 받는다.
기어 판정: RPM > 3000rpm이면 고속 운전

문제: CAN 메시지 지연 + 엔진 진동으로 RPM이 2950~3050 진동

해결책: 히스테리시스 적용

설정:
- Upper: 3100rpm (고속 판정 시작)
- Lower: 2900rpm (저속 판정 시작)

결과:

RPM 신호:
2800 → 2950 → 3050 → 3100 → 3050 → 3000 → 2950 → 2900

상태:
저    저     저     고속    고속    고속    고속    저
      (3000미달) (3100초과) (2900미달)
                 ↑ 여기서 전환     ↑ 여기서 전환

5. 하드웨어 구현

5.1 슈미트 트리거 (Schmitt Trigger)

회로도:

입력 신호
  │
  ├──[R1]──┬─[Op-Amp]──┬──→ 출력 신호
           │           │
          [C1]    [R2]─┴─Feedback
           │           │
          GND         GND

특성:
- 입력이 Upper Threshold를 넘으면 → 출력 1
- 입력이 Lower Threshold 아래로 내려가야 → 출력 0
- 두 임계값 사이에서는 이전 상태 유지

Upper Threshold = Vcc × R2 / (R1 + R2)
Lower Threshold = Vcc × R3 / (R1 + R3)
(정확한 계산은 Op-Amp 특성에 따라 다름)


동작:

입력 신호:          출력 신호:
┌────────────┐     1 ┌──────────────┐
│    4V      │       │              │ 1
│   ╱────╲   │       │        ┌─────┘
│  ╱      ╲  │       │        │
│ ╱ 3.5V   ╲ │       │    ┌───┘
│╱ 2.5V     │0 ──────┴────┴─────
  ↑         ↑
  │         └─ Upper (3.5V 초과)
  └─ Lower (2.5V 미만)

채터링 제거됨! (HW에서 자동 처리)

5.2 RC 필터 + 비교기

회로도:

입력 ──[R]──┬──→ 비교기 (+)
            │
          [C]   참조 (-)
            │   ↓ (Vref = 임계값)
           GND

출력: 디지털 신호 (0 또는 1)

동작:
RC 필터: 입력 신호의 급격한 변화 완화
비교기: Filtered vs Vref 비교

효과:
- RC 필터가 노이즈 제거
- 비교기가 깔끔한 0/1 신호 생성

6. AUTOSAR에서의 히스테리시스

6.1 RTE 설정 (*.arxml)

<!-- AUTOSAR 4.2 예시 -->
<PortInterface Name="EngineTemperatureInterface">
  <Port Name="EngineTemp">
    <Type>AnalogInput</Type>

    <!-- 히스테리시스 설정 -->
    <Hysteresis>
      <Type>SwitchingThreshold</Type>
      <HigherThreshold>95.0</HigherThreshold>
      <LowerThreshold>90.0</LowerThreshold>
    </Hysteresis>

    <!-- 디지털 상태로 변환 -->
    <BooleanPort>
      <Name>IsOverheated</Name>
    </BooleanPort>
  </Port>
</PortInterface>

6.2 C 코드에서 사용

// AUTOSAR RTE를 통한 자동 필터링
void EngineControl_MainFunction(void) {
    // RTE에서 자동으로 히스테리시스 적용한 값 반환
    Std_ReturnType status = Rte_Read_EngineTemp_IsOverheated(&overheating);

    if (status == RTE_OK && overheating == TRUE) {
        // 과열 상태 (히스테리시스로 안정화됨)
        EngineControl_ReducePower();
        SetWarningLight();
    }
}

7. 성능 분석

7.1 응답 시간 vs 안정성

히스테리시스 간격과 성능:

간격 = 1°C (좁음):
├─ ✓ 빠른 응답 (거의 원래 임계값과 비슷)
├─ △ 여전히 노이즈에 취약
└─ ❌ 안정성 개선 미흡

간격 = 5°C (표준):
├─ ✓ 빠른 응답 (5°C 정도 지연)
├─ ✓ 노이즈 제거
└─ ✓ 안정성 좋음 (권장!)

간격 = 10°C (넓음):
├─ △ 응답 지연 (10°C)
├─ ✓ 완전한 노이즈 제거
├─ ✓ 안정성 최고
└─ ❌ 운전자가 반응 지연 느낄 수 있음


추천 히스테리시스 간격:

┌─────────────────────┬──────────┐
│ 신호 종류           │ 간격     │
├─────────────────────┼──────────┤
│ 온도 센서           │ 3~5°C    │
│ 전압 센서           │ 0.5~1V   │
│ RPM 신호            │ 200~500  │
│ 압력 센서           │ 0.5~2bar │
│ 디지털 신호         │ N/A      │
└─────────────────────┴──────────┘

7.2 CPU 부하 감소

일반 임계값:
┌──────────────────────────────┐
│ 매 샘플마다 비교 연산        │
│ temp > 95.0 ? (매번 실행)    │
└──────────────────────────────┘
CPU 부하: 100%

히스테리시스:
┌──────────────────────────────┐
│ 경계값을 벗어날 때만 비교    │
│ temp > 95.0 || temp < 90.0   │
└──────────────────────────────┘
CPU 부하: ~30% (70% 절감!)

8. 자주 하는 실수

8.1 간격을 너무 크게 설정

❌ 잘못된 예:
#define TEMP_UPPER 95.0f
#define TEMP_LOWER 80.0f  // 간격 15°C는 너무 크다!

문제:
- 80°C: 정상
- 95°C: 과열
- 90°C: 여전히 과열 (15°C까지 유지)
- 온도가 90°C로 내려와도 경고 안 해제
- 운전자가 불안감 느낌

✅ 올바른 예:
#define TEMP_UPPER 95.0f
#define TEMP_LOWER 90.0f  // 간격 5°C

8.2 상태를 추적하지 않음

❌ 잘못된 예:
uint8_t IsOverheated(float temp) {
    // 상태 변수 없음!
    if (temp > 95.0f) {
        return 1;
    } else if (temp < 90.0f) {
        return 0;
    }
    // 90~95°C 사이: ???
    return 0;  // 무조건 0 반환 (잘못됨!)
}

문제:
- 90~95°C 사이에서 항상 0 (정상)
- 히스테리시스 동작 안 함
- 경계값 근처에서 불안정

✅ 올바른 예:
static uint8_t state = 0;

uint8_t IsOverheated(float temp) {
    if (state == 0) {
        if (temp > 95.0f) {
            state = 1;
        }
    } else {
        if (temp < 90.0f) {
            state = 0;
        }
    }
    return state;  // 상태 추적!
}

8.3 멀티코어 동기화 누락

❌ 잘못된 예:
static uint8_t engine_state = 0;  // 전역 변수

void Task_A(void) {  // Core 0
    float temp = ReadTemperature();
    if (engine_state == 0 && temp > 95.0f) {
        engine_state = 1;  // 쓰기
    }
}

void Task_B(void) {  // Core 1
    if (engine_state == 1) {  // 읽기
        ReduceEnginePower();
    }
}

문제:
- Race condition 발생 가능
- engine_state 값이 예측 불가능

✅ 올바른 예:
static uint8_t engine_state = 0;
static SPINLOCK_t state_lock;

void Task_A(void) {
    SPINLOCK_ACQUIRE(&state_lock);
    float temp = ReadTemperature();
    if (engine_state == 0 && temp > 95.0f) {
        engine_state = 1;
    }
    SPINLOCK_RELEASE(&state_lock);
}

void Task_B(void) {
    SPINLOCK_ACQUIRE(&state_lock);
    if (engine_state == 1) {
        ReduceEnginePower();
    }
    SPINLOCK_RELEASE(&state_lock);
}

8.4 경계값 설정 오류

❌ 잘못된 예:
#define TEMP_UPPER 95.0f
#define TEMP_LOWER 96.0f  // Lower > Upper (불가능!)

문제:
- 논리적 오류
- 상태 전환 불가능
- 영원히 정상 상태

✅ 올바른 예:
#define TEMP_UPPER 95.0f
#define TEMP_LOWER 90.0f  // Lower < Upper (항상!)

9. 히스테리시스 vs 다른 기법 비교

기법            안정성  응답속도  구현난도  비용
────────────────────────────────────────────
단순 임계값      낮음    빠름    쉬움     저
히스테리시스    높음    중간    중간     저
다중 샘플링      높음    느림    중간     저
RC 필터         높음    느림    중간     중간
슈미트 트리거    최고    빠름    어려움   중
────────────────────────────────────────────

추천:
SW 구현: 히스테리시스 + 타이머 (가성비 최고)
HW 구현: 슈미트 트리거 (안정성 최고)
혼합:    RC 필터 + 히스테리시스 (최적)

10. 핵심 정리

히스테리시스(Hysteresis):
- 경계값 근처의 진동을 방지하는 기법
- 상태 전환에 두 개의 경계값 사용
- 이전 상태에 따라 현재 동작 결정

필요성:
- 센서 노이즈 제거
- 상태 깜빡임 방지 (경고등 등)
- CPU 부하 감소
- 시스템 안정성 향상

구현 방법:
- SW: 상태 변수 + 조건문
- HW: 슈미트 트리거 회로
- AUTOSAR: RTE 자동 필터링

경계값 설정:
- Upper: 올라갈 때 (더 높음)
- Lower: 내려갈 때 (더 낮음)
- 간격: 3~5°C (온도 기준)

주의사항:
- Lower < Upper 항상 만족
- 멀티코어 동기화 필수
- 상태 변수 추적 필수
- 적절한 간격 선택

성능:
- 응답 시간: ~5°C 지연 (무시할 수준)
- CPU 부하: 30~50% 감소
- 신뢰성: 95% 이상 향상