seukseok의 개인 블로그

C++로 수집하고 Python으로 학습하고 Rust로 배포한 CAN 이상탐지 파이프라인

· tech

CAN 로그 기반 이상탐지 파이프라인을 C++ 수집, Python 학습, Rust 추론 서비스로 나눠 설계하는 방법과, 실무에서 흔한 컴파일·런타임 트러블슈팅 포인트를 예시와 함께 설명한다.

결론부터 말하면, CAN 이상탐지는 모델보다 파이프라인 설계가 성패를 가른다.

특히 학생 프로젝트나 랩 과제에서 자주 보는 실패 패턴은 이렇다.

이번 글은 이걸 줄이기 위한 최소 실전 구성을 다룬다.

1) 역할 분리: C++ 수집, Python 학습, Rust 배포

내가 추천하는 구조는 단순하다.

  1. C++: SocketCAN으로 고속/저지연 수집
  2. Python: 피처 엔지니어링 + 학습/검증
  3. Rust: 안정적인 추론 서비스(메모리 안정성 + 배포 편의)

한 언어로 다 하는 것도 가능하지만, 실습/연구 단계에서는 이 분리가 디버깅 속도를 확실히 올려준다.

2) C++ 수집에서 제일 중요한 것: 시간 정렬

CAN 프레임을 읽는 코드는 어렵지 않다. 문제는 시간축 품질이다.

// g++ -O2 can_capture.cpp -o can_capture
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>

// 핵심: 프레임 내용보다 timestamp 일관성을 먼저 확보

실무 팁:

확인 필요: 차량/플랫폼별로 하드웨어 타임스탬프 지원 방식이 다르므로, 사용하는 CAN 인터페이스 문서를 먼저 확인해야 한다.

3) Python 학습: 거창한 모델보다 “윈도우 특징”이 먼저

초기에는 LSTM/Transformer부터 가고 싶지만, CAN 이상탐지의 1차 베이스라인은 통계 특징 + 경량 모델이 더 빠르다.

# 예시: 100ms window 기준 간단 특징
features = {
    "msg_rate": len(window),
    "id_entropy": entropy(window["can_id"]),
    "dlc_mean": window["dlc"].mean(),
    "inter_arrival_std": window["dt_ms"].std(),
}

모델은 Isolation Forest나 One-Class SVM부터 시작해도 충분하다. 중요한 건:

4) Rust 배포: “안 터지는 서비스” 만들기

Rust 쪽은 tokio 기반 마이크로서비스로 두면 관리가 쉽다.

// Cargo.toml: tokio, serde, axum(or actix-web)
// 핵심: 입력 검증 + timeout + backpressure

운영에서 유용한 포인트:

이렇게 해야 “왜 오늘 갑자기 오탐이 늘었는지”를 추적할 수 있다.

5) 자주 겪는 트러블슈팅

(a) C++ 컴파일 에러

fatal error: linux/can.h: No such file or directory

원인: Linux CAN 헤더 패키지 미설치 혹은 비리눅스 환경. 해결: Linux 환경(또는 WSL2 + 적절한 커널/헤더)에서 빌드, 패키지 설치 확인.

(b) Python 결과 재현 실패

같은 코드인데 성능이 달라지면, 보통 아래 둘이다.

(c) Rust 릴리즈 빌드 후 지연시간 급증

디버그 빌드와 릴리즈 빌드를 혼용하면 측정이 틀어진다.

cargo build --release

그리고 CPU governor/전원 모드 고정 없이 벤치마크하면 수치가 흔들린다.

6) 최소 체크리스트

여기까지 맞추면, 데모용이 아니라 실제 차량/로봇 실험에서도 꽤 버틴다.

참고 자료

마무리

CAN 이상탐지는 모델 한 방으로 끝나지 않는다. 데이터 품질, 운영 지표, 배포 안정성을 함께 설계하면 결과가 훨씬 덜 흔들린다.

다음 편에서는 동일 파이프라인에 “간단한 회로 레벨 센서 이상(전압 드리프트)” 신호를 합쳐서 멀티모달 탐지로 확장하는 방법을 다루겠다.