제7부 실전: ROS2 Humble 프로젝트
7.9 안전·결함 처리
보강 장 (v1 이후 추가) · 논리적 위치: 제7부 실전, 실로봇 배치의 필수. 제어 이론은 정상일 때 성능을 보장한다 — 그런데 센서가 끊기고·액추에이터가 고장나고·통신이 두절되고·소프트웨어가 멈추면? 안전·결함 처리는 잘못됐을 때 무슨 일이 일어나는가를 설계한다. 실로봇·실제품의 필수 요건.
✅ 검증됨: 혁신 기반 결함 감지(카이제곱 게이팅)를
ros:humble컨테이너에서 실행해 검증(verify/ce43_safety.py) — 센서 이상치를 기각해 추정 RMS 3.4배 개선.
이 장을 마치면 리밋·워치독·E-stop을 구분하고, 칼만 혁신으로 센서 결함을 감지하며, "정상→저하→안전" 상태기계로 페일세이프를 설계할 수 있어야 한다.
7.9.1 왜 안전 — 제어는 정상 조건을 전제한다
이 책의 모든 설계(PID·LQR·칼만·MPC)는 센서가 정상이고·액추에이터가 명령대로 움직이고·루프가 제때 실행된다는 전제에 기반했다. 현실에서는 이 전제가 성립하지 않는다:
- 센서 결함: 드롭아웃·글리치·고착(stuck)·바이어스·잡음 급증.
- 액추에이터 결함: 포화·고장·응답 없음·과전류.
- 통신·계산: 토픽 두절(7.1), 노드 행(hang), 마감 초과(3.3.9).
제어 성능을 0.001 개선하는 것보다, 고장 시 다치지 않게 만드는 것이 실제품에서는 훨씬 중요하다. 안전은 "성능"이 아니라 "무엇이 잘못될 수 있고, 그때 어떻게 안전해지나"다.
7.9.2 리밋 — 경계를 강제한다
가장 기본. 위치·속도·토크를 물리·안전 한계 안으로 강제한다.
- 하드 리밋(클램프): 명령·상태를 한계로 자른다(
u=clamp(u, u_min, u_max), 2.5.6.3의 포화). 절대 넘으면 안 되는 선. - 소프트 리밋: 한계 접근 시 미리 감속(완충 구간). 충격 없이 멈춤.
- 이중화: 소프트웨어 리밋 + 하드웨어 리밋 스위치·기계 스토퍼(소프트웨어가 실패해도).
리밋은 모든 계층에 둔다 — 궤적 생성(7.8 제약)·제어 출력(클램프)·하드웨어(스토퍼). 한 계층이 실패해도 다음 계층이 차단하게.
7.9.3 워치독 — 멈춤을 감지한다
소프트웨어가 행(hang)되거나 통신이 끊기면 마지막 명령이 그대로 유지돼 위험하다(폭주). 워치독(watchdog): 주기적으로 "살아 있다"고 킥(kick)해야 하는 타이머. 제때 킥이 안 오면 → 페일세이프 발동(정지·안전상태).
ROS2에서는 토픽 타임스탬프·/heartbeat로 두절을 감지한다. 예: /cmd_vel이 0.2초 이상 안 오면 로봇 정지(많은 이동로봇 드라이버의 기본 안전장치).
타임아웃 튜닝: 값 하나를 정하는 문제가 아니라 두 실패 모드 사이의 타협이다.
- 너무 짧으면 오탐(false positive): 정상적인 지터·스케줄링 지연에도 발동된다. 예: 제어 루프가 공칭 50ms 주기로 퍼블리시하지만 부하가 걸리면 가끔 80ms까지 지연되는 시스템에서 타임아웃을 100ms로 설정하면 여유가 20ms뿐이다 — 정상 지터에도 빈번하게 페일세이프가 발동해 로봇이 멈춘다.
- 너무 길면 감지가 늦다: 실제 결함(노드 행·통신 두절)이 나도 상당히 늦게 검출된다 — 그 사이 로봇은 오래된(stale) 명령으로 계속 움직이는 위험한 창(window)이 늘어난다.
- 경험칙: 타임아웃 ≈ 공칭 주기의 3~5배, 그리고 이것은 출발점일 뿐 — 실측한 지터 분포(최악 관측치가 아니라 꼬리 분포)를 보고 조정해야 한다. 위 예라면 공칭 50ms의 3~5배는 150~250ms — 관측된 최악 지터 80ms에 충분한 여유(150ms면 약 2배)를 준다. 100ms는 이 경험칙보다 짧아 위험하다.
7.9.4 E-stop — 즉시 안전하게
비상정지(emergency stop)는 즉시 시스템을 안전 상태로. 표준 범주:
- Cat 0: 즉시 전원 차단(가장 안전하나 제어 없이 멈춤 — 관성으로 미끄러질 수 있음).
- Cat 1: 제어된 감속 후 전원 차단(부드럽게 멈추고 끔).
- Cat 2: 제어 유지한 채 정지(전원 유지).
⚠️ E-stop은 하드웨어가 핵심이다 — 소프트웨어 E-stop만으로는 부족(소프트웨어가 중단되면 무용). 물리 버튼 + 안전 릴레이가 액추에이터 전원을 직접 끊어야. 기능안전 표준(ISO 13849, IEC 61508)이 이를 규정.
7.9.5 결함 감지 — 무언가 잘못됐음을 안다
고장에 대응하려면 먼저 감지해야 한다.
- 센서 점검: 범위($\min\le z\le\max$)·변화율(
|ż|한계)·타당성(plausibility)·고착(값이 안 변함)·이중화 투표(센서 여러 개 비교). - 혁신 모니터링(칼만): 칼만 필터의 혁신
ỹ(측정−예측)이 그 공분산 $S$보다 지나치게 크면 측정이 이상하다는 신호(5.2.8·5.3.7). 카이제곱 게이팅:ỹ²/S > 임계값이면 그 측정을 기각. 추정기가 곧 결함 감지기가 된다.
게이팅의 핵심은 칼만 갱신 직전에 삽입하는 if문 하나다(verify/ce43_safety.py에서 발췌, GATE=9.0은 1자유도 카이제곱의 $3\sigma$ 기준):
GATE = 9.0 # 카이제곱 1DOF 3σ ≈ 9
S = (C@[email protected] + R)[0, 0] # 혁신 공분산
yt = z - (C@x)[0] # 혁신 ỹ = 측정 − 예측
nis = yt*yt / S # 정규화 혁신 제곱(NIS)
if nis > GATE: # 결함으로 보고 기각
pass # 갱신을 건너뜀 — 예측만으로 진행
else:
K = ([email protected] / S) # 정상 측정 → 평소대로 칼만 갱신
x = x + (K*yt).flatten()
P = (np.eye(2) - K@C)@P
- 액추에이터: 명령 대 응답 불일치, 과전류.
- 모델 기반: 잔차(residual)로 어느 부품이 고장났는지 격리.
검증 결과 (센서 글리치에 카이제곱 게이팅):
게이팅 OFF 위치 RMS 오차 = 0.684 (이상치가 추정 오염)
게이팅 ON 위치 RMS 오차 = 0.201 (이상치 10개 기각) → 3.4배 개선
→ CE43_FAULT_PASS
혁신이 $3\sigma$($\tilde{y}^2/S>9$)를 넘는 측정을 결함으로 보고 기각하니, 센서 글리치에도 추정이 안정적으로 유지됐다. 5.2·5.3의 칼만이 추정만이 아니라 결함 감지에도 쓰인다.
7.9.6 페일세이프 — "안전"의 정의는 시스템마다
감지했으면 안전 상태로 가야 한다. 무엇이 "안전"인지는 시스템에 달렸다:
- 이동로봇: 정지(stop). 로봇팔: 현 위치 유지(hold) 또는 천천히 힘 빼기.
- 드론: 멈추면 추락 — 제어된 하강·귀환(RTL)이 안전.
- 차량: 갓길 정차.
상태기계로 단계적 대응한다 — 그리고 처음 생각처럼 "악화되면 한 방향으로만" 가서는 안 된다. 결함이 사라지면 돌아올 길이 있어야 하고, 그 복귀가 문턱값 근처에서 진동하며(chatter) 자꾸 왕복하지 않게 해야 한다:
- 왜 즉시 양방향 전환이면 안 되나: "혁신이 임계값을 넘으면 저하, 밑돌면 즉시 정상"처럼 같은 임계값으로 즉시 양방향 전환을 만들면, 신호가 임계값 근처에서 잡음으로 오르내릴 때마다 정상↔저하를 빠르게 왕복한다(채터링) — 실제로는 정상인데 상태만 깜빡이며, 그때마다 범프리스 전환 비용(아래)까지 반복된다.
- 표준 해법 1 — 디바운스(dwell time): 상태가 바뀌려면 새 조건이 한 순간이 아니라 일정 시간(또는 연속 N회) 유지돼야 한다. 위 그림의 "연속 N회 정상"이 그것 — 하트비트·측정이 한 번 좋아졌다고 바로 복귀하지 않고, N번 연속 좋아야 복귀한다.
- 표준 해법 2 — 히스테리시스 밴드: 진입 임계값
T_in과 복귀 임계값T_out을 다르게(T_out < T_in) 둔다. 저하로 들어가는 문턱과 정상으로 나오는 문턱 사이에 밴드를 두면, 신호가 밴드 안에서 변동해도 상태는 안 바뀐다 — 두 조건(디바운스+히스테리시스)을 같이 쓰는 것이 흔하다. - 저하→안전은 보통 자동 복귀가 없다: 안전 상태(정지·귀환)까지 간 뒤에는 사람이 원인을 확인하고 수동 리셋하는 것이 일반적이다 — 자동으로 저하·정상까지 되돌아가면, 실제 원인을 못 찾은 채 같은 결함이 반복될 위험이 있다.
- 우아한 저하(graceful degradation): 한 센서를 잃어도 나머지로 제한 운행(예: GPS 잃으면 오도메트리로 단기, 7.5). 즉시 멈추기보다 안전하게 운용을 유지한다.
- 범프리스 전환(2.5.6.4): 모드 전환(정상↔안전, 자동↔수동) 시 출력이 튀지 않게 — 적분기·상태 초기화.
7.9.7 정직 — 안전은 별도 분야다
- 시스템 특정적: "안전"의 정의·페일세이프는 시스템마다 다르다 — 일반 공식이 없다. 위험 분석(HAZOP·FMEA)으로 도출.
- 하드웨어가 근본: 소프트웨어 안전은 소프트웨어가 살아 있을 때만. 독립적 하드웨어(안전 릴레이·기계 스토퍼·하드웨어 워치독)가 최후 방어선.
- 기능안전 표준: 실제품은 ISO 13849·IEC 61508·ISO 26262(차량) 등을 따른다 — 이 장은 개념·진입점이며 인증·규정은 전문 영역.
- 검증의 한계: 이 장 코드(혁신 게이팅)는 결함 감지의 한 조각을 보일 뿐 — 완전한 안전 시스템은 훨씬 광범위하다(이 책 범위 밖).
7.9.10 ROS2 통합 — diagnostic_updater와 라이프사이클 노드
지금까지의 워치독·상태기계는 개념과 최소 구현이다. 실제 ROS2 시스템에는 이를 표준화한 두 축이 있다 — 보고(diagnostics)와 공식 상태 전이(lifecycle).
diagnostic_updater/diagnostic_msgs: 각 컴포넌트(센서 드라이버·제어 노드·배터리 등)가 자신의 상태를DiagnosticStatus($OK$/WARN/ERROR+ 메시지)로 주기적으로 퍼블리시하는 표준 패턴이다.diagnostic_updater::Updater에 점검 함수를 등록해두면(예: "혁신 게이팅 기각률", "워치독 최근 킥 이후 경과 시간") 그 함수가 상태를 판정해/diagnostics에 발행한다. 여러 컴포넌트의/diagnostics를diagnostic_aggregator가 트리 구조(하드웨어별·기능별)로 묶어 "로봇 어디가 저하됐는가"를 한눈에 보여준다. 7.9.6의 상태기계가 결정(정상/저하/안전으로 무엇을 할지)이라면,diagnostic_updater는 그 판단의 근거가 되는 보고를 표준화한다 — 이 장의 애드혹/heartbeat하나를 실전에서는 컴포넌트별DiagnosticStatus로 세분화하는 셈이다.- 라이프사이클 노드: ROS2 노드는
unconfigured → inactive → active → finalized(+ 전이 중간상태)라는 표준 상태기계를 가질 수 있다(rclcpp_lifecycle). 이 장의 "저하"·"안전"을 애드혹 불리언 플래그로 표현하는 대신, 라이프사이클 상태 전이로 공식화할 수 있다 — 예: 결함 감지 시active → inactive로 전이해(제어 출력을 정지하되 상태는 보존) 7.9.6의 "저하" 대응을 표준 API로 구현하는 식. 라이프사이클 상태·전이 콜백·ros2_control과의 결합 같은 세부 메커니즘은 7.1.5를 참고하라 — 여기서는diagnostic_updater가 그 라이프사이클 상태를 보고하는 짝이라는 것만 언급한다. 상위 모니터링은 노드의 라이프사이클 상태와/diagnostics를 함께 봐야 전체 그림이 나온다.
7.9.8 정리 — 그리고
- 제어는 정상 조건을 전제한다 — 안전·결함 처리는 고장 시 안전을 설계한다(7.9.1).
- 리밋(소프트·하드·이중화)·워치독(멈춤 감지→페일세이프)·E-stop(즉시 안전, 하드웨어 핵심)이 기본 방어층(7.9.2~7.9.4).
- 결함 감지: 센서 점검·칼만 혁신 게이팅(검증: 글리치 기각 3.4배 개선)·액추에이터 불일치(7.9.5). 감지 후 상태기계(정상→저하→안전)로 우아한 저하·범프리스 전환(7.9.6).
- 안전은 시스템 특정적이고 하드웨어가 근본이며 기능안전 표준을 따른다 — 이 장은 진입점(7.9.7).
- 실전에서는 애드혹 플래그 대신
diagnostic_updater(컴포넌트별 OK/WARN/ERROR 보고)·라이프사이클 노드(공식 상태 전이, 7.1.5)로 표준화한다(7.9.10).
이로써 제7부 보강(제어 구조·궤적 생성·안전)을 마친다 — 이어 7.10(로봇팔 관절공간 제어)가 완전구동 MIMO라는 새 실전 프로젝트로 보강 장을 마무리한다. 제어 이론(제1~6부)·실전 구현(7.1~7.6)에 더해, 부드러운 기준 생성(7.8)·구조적 보강(7.7)·고장 대비(7.9)·완전구동 MIMO 실전(7.10)까지 — 실제품에 한 걸음 더 가까워졌다.
7.9.9 연습·생각해 볼 것
- 리밋 계층. 소프트웨어 클램프만 있고 하드웨어 리밋이 없으면 어떤 고장에 취약한가? 왜 모든 계층에 리밋을 두나? (7.9.2)
- 워치독. 이동로봇이
/cmd_vel두절 시 정지해야 한다. 워치독을 어떻게 구현하나(킥·타임아웃)? 두절을 감지 못 하면 무슨 일이? (7.9.3) - E-stop. 소프트웨어 E-stop만으로 부족한 이유는? Cat 0과 Cat 1의 차이와, 각각 어떤 로봇에 적합한지 설명하라. (7.9.4)
- 혁신 게이팅. 칼만 혁신 $\tilde{y}^2/S$가 큰 것이 왜 "측정이 이상하다"는 신호인가? 임계값을 너무 낮게(민감) 설정하면? 너무 높게(둔감) 설정하면? (7.9.5)
- 페일세이프 정의. 이동로봇·로봇팔·드론·차량 각각에 "안전 상태"가 무엇인지, 왜 다른지 설명하라. 드론이 정지하면 안 되는 이유는? (7.9.6)
- 우아한 저하. GPS를 잃은 자율로봇이 즉시 멈추는 것과 오도메트리로 잠시 운용을 유지하는 것 중 언제 어느 쪽이 안전한가? 범프리스 전환이 왜 필요한가? (7.9.6, 7.5)
- (손으로)
verify/ce43_safety.py로 게이팅 유무를 비교하고, 게이팅 임계값(GATE)을 바꿔 가며 이상치 기각률·추정 오차를 관찰하라. 정상 측정까지 기각되기 시작하는 임계값은? (검증된 스크립트)
막히면 "리밋·워치독·E-stop이 방어층, 칼만 혁신이 결함 감지, 정상→저하→안전 상태기계, 하드웨어가 근본"으로 돌아가라.