무엇을 어떻게 판단하는지, 그리고 그 판단을 무엇으로 뒷받침했는지.
여기 적힌 값은 모두 실제로 측정한 것입니다. 관례나 통념으로 정한 것은 그렇다고 밝혔습니다. 우리가 틀렸던 것도 함께 적었습니다 — 같은 함정을 다시 밟지 않기 위해서입니다.
계산은 현장에서 하고, 판단 기준은 중앙에서 정합니다.
원파형은 서버로 가지 않습니다. 1초 분량이 100 KB인데 판정 결과는 82 B입니다. 약 1,250배 차이라, 2,756대 규모에서 하루 95 GB가 0.9 GB가 됩니다. 규모가 커지면 이 방법 말고는 감당할 수가 없습니다.
판정에는 diag_tier가 함께 붙습니다. 어디까지 계산해서 나온 결론인지를 알려 줍니다.
| 계층 | 볼 수 있는 것 | 필요한 것 |
|---|---|---|
| MCU | 시간 영역만 봅니다 — 이미 진행된 불평형·과부하 | 없음(정수 연산) |
| 게이트웨이 | 포락 분석과 이상탐지 — 베어링 결함까지 | numpy·scipy·sklearn 196 MB |
| 서버 | 설비끼리 비교, 추세 보기 | — |
시간영역만 보는 장치가 "정상"을 올리면 화면에 초록불이 켜집니다. 그런데 그 장치는 애초에 베어링 결함을 볼 수 없습니다. 못 본 것과 없는 것은 다릅니다. 일반 사용자 화면에서는 이걸 "간이 감시 / 정밀 감시"로 옮겨 보여 줍니다.
베어링이 상하면 일정한 간격으로 충격이 생깁니다. 그 간격이 뚜렷할 때만 어디가 상했는지 말합니다.
베어링 치수(볼 개수·볼 지름·피치 지름)로 회전마다 나타날 충격 간격을 계산하고, 포락 스펙트럼에서 그 간격의 성분이 얼마나 큰지 봅니다. 가장 큰 성분이 차순위보다 4배 이상 뚜렷할 때만 부위를 지목합니다.
임계를 못 넘으면 diag_part를 비웁니다. 빈칸이 틀린 답보다
낫습니다. 대신 diag_route에 "BPFO 최대이나 차순위 대비 1.6배 — 부위를
지목하지 않음"처럼 이유를 남겨 사람이 검증할 수 있게 합니다.
베어링 치수를 모르면 이 단계 전체가 소용없습니다. 치수가 틀리면 찾아야 할 주파수가 틀리고, 결국 엉뚱한 곳을 보게 됩니다. 그래서 현장 세팅에서 제원 입력을 사람 손에 맡기지 않고 템플릿이 자동으로 내려보냅니다.
원인을 몰라도 "평소와 다르다"는 말할 수 있습니다. 기준선과 얼마나 떨어졌는지로 봅니다.
서로 다른 원리의 탐지기 8종(마할라노비스·kNN·LOF·PCA 재구성·오토인코더· IsolationForest·OneClassSVM·최대 z)이 각자 판정하고, 몇 종이 동의했는지를 봅니다. 한 알고리즘이 가진 버릇에 끌려가지 않기 위해서입니다.
오탐률을 맞추고 비교한 탐지율입니다.
| 방식 | 오탐 | 탐지 |
|---|---|---|
| 8종 합의 | 5% | 100% |
| 마할라노비스 단독 | 10% | 100% |
| kNN 단독 | 10% | 100% |
| PCA 재구성 | 10% | 97% |
| 오토인코더 | 10% | 40% |
ECOD·Extended IsolationForest는 측정 후 기각했습니다. 표준 IsolationForest보다 분리가 나빴습니다.
| 구성 | σ 중앙값 | 3σ 넘긴 탐지기 |
|---|---|---|
| 반응하는 채널 1개 | 9.5 | 7/8 |
| 반응하는 채널 4개 | 8.1 | 8/8 |
| + 무관한 채널 3개 | 2.7 | 3/8 |
| + 무관한 채널 19개 | 1.7 | 2/8 |
위 결과를 보고 "쓸모없는 채널 3개면 무너진다"고 적었는데, 그건 반응하는 채널이 하나뿐인 취약한 조건에서 잰 값이었습니다. 실제 진동 특징량으로 다시 재니 달랐습니다.
| 구성 | 채널 | 오탐 | 탐지 |
|---|---|---|---|
| 실제 특징량만 | 9 | 10% | 55% |
| + 무관한 잡음 3개 | 12 | 10% | 55% |
| + 무관한 잡음 9개 | 18 | 10% | 38% |
무관한 채널이 해로운 것은 맞지만 비율의 문제입니다. 쓸 만한 특징량이 충분하면 몇 개 섞여도 견딥니다.
| 채널 묶음 | 채널 | 오탐 | 탐지 |
|---|---|---|---|
| 전부 넣기 | 9 | 10% | 75% |
| 베어링 관련만 골라 넣기 | 4 | 10% | 55% |
첨도처럼 그 자체로는 거의 안 움직이는 값도 빼면 손해입니다. 같은 신호에서 나온 값들은 서로 연관돼 있고, 마할라노비스는 그 연관 구조에서 정보를 얻기 때문입니다. "평소엔 같이 움직이던 둘이 따로 논다"가 신호가 됩니다.
정리하면 설비에서 나온 값은 다 넣고, 설비와 무관한 값만 뺍니다. 그리고 아예 변하지 않는 채널은 반드시 뺍니다 — 정보가 없을 뿐 아니라 표준편차가 0이라 나눗셈이 깨집니다.
"그 구간이 정상이었다"는 데이터로 증명할 수 없습니다.
처음부터 나빴을 수도 있기 때문입니다. 그래서 화면에 기준선 구간을 함께 적고,
정비 이력이 있으면 그쪽을 우선합니다. 기준선이 바뀌면
baseline_status도 함께 올려 이전 판정과 섞이지 않게 합니다.
보이지 않는 것은 예측할 수 없습니다. 이게 전부입니다.
같은 데이터에 순수 ML·통계 추세·물리 제약을 같은 조건으로 걸었습니다.
| 조건 | ML | 물리 제약 |
|---|---|---|
| 관측 가능 (시간–건강지표 상관 ρ>0.3) | 199% | 99% |
| 관측 불가 | ~200% | ~200% |
| XJTU (RMS가 수명 93~97%까지 평평) | 오차 104~107% | |
물리 제약은 관측 가능할 때만 2배 개선입니다. 급작스러운 고장에는 어떤 방법도 소용없습니다. 막히는 곳은 모델 성능이 아니라 애초에 신호에 드러나느냐입니다.
그래서 잔여수명은 켜고 끄는 기능이 아닙니다. 신호에 드러나는지 먼저 확인하고, 통과한 설비에만 붙입니다.
현장은 계산하고, 중앙은 기준을 정합니다.
| 방향 | 무엇이 | 어떻게 |
|---|---|---|
| 서버 → 엣지 | 기준선·임계·베어링 제원·보고 주기·모델 | TB 공유 속성 |
| 엣지 → 서버 | 판정 결과 14키 | TB 텔레메트리 |
| 방식 | 현장에 올라가는 것 | 하루 전송 |
|---|---|---|
| 우리 게이트웨이 | 196 MB | 1.4 MB |
| ThingsBoard Edge | 약 4.5 GB | 동일 |
TB Edge의 핵심 가치는 통신 두절 시 버퍼링인데, 우리가 버퍼링할 양은 24시간에 1.4 MB입니다 — 파일 하나면 됩니다. 그리고 TB Edge는 자바 룰엔진이라 포락 분석과 이상탐지를 대신해 주지 못합니다.
통신이 끊긴 동안 현장에서 제어·차단이 필요하거나 역무실에 자체 화면이 필요해지면, 그때는 TB Edge가 맞습니다. 계약이 그대로라 서버 쪽은 바꿀 것이 없습니다.
진단(베어링 결함 주파수·포락·사이드밴드)은 원파형이 있어야 합니다. 판정 14키로는 “이상하다”까지만 말할 수 있고 “왜”는 말하지 못합니다. 그렇다고 원파형을 계속 쌓을 수는 없습니다.
| 방식 | 한 조각 | 하루 100건 | 1년 |
|---|---|---|---|
| 계속 저장 | 50 KB/초 | 4.3 GB/일 | 1.6 TB |
| float32 조각 | 50.0 KB | 4.9 MB | 1.74 GB |
| int16 조각 | 25.0 KB | 2.4 MB | 0.87 GB |
gzip 은 쓰지 않습니다 — 진동 잡음은 압축이 되지 않아 25 KB 그대로였습니다. 재 보고 뺐습니다.
그래서 이상으로 판정된 순간의 1초짜리 조각만 남깁니다.
엣지가 이미 그 시점을 알고 있으므로, 판정을 올릴 때 조각을 함께 올리면 됩니다.
저장은 텔레메트리(ts_kv)가 아니라 우리 스키마에 둡니다 — TB 텔레메트리는
스칼라를 위한 자리라, 25 KB 문자열을 밀어 넣으면 하이퍼테이블이 부풀고
TB 화면이 그 값을 그리려다 멎습니다.
하루 100건이면 1년에 0.87 GB 지만, 500건이면 4.34 GB 입니다. 설비가 늘거나 임계가 헐거워지면 건수는 쉽게 늘어납니다. 건수·일수 상한을 걸고, 사람이 ‘맞음’ 이라 표시한 것은 상한에서 뺍니다 — 그게 나중에 다시 볼 유일한 자료이기 때문입니다.
판정은 이미 82 B라 더 줄일 것이 없습니다. 남은 방법은 얼마나 자주 보내느냐뿐입니다. 정상이면 뜸하게, 이상하면 촘촘하게 보냅니다.
| 상황 | 매번 | 조절 시 | 감소 |
|---|---|---|---|
| 하루 종일 정상 | 1,440 | 144 | 10.0배 |
| 18시간 뒤 고장 시작 | 1,440 | 239 | 6.0배 |
| 처음부터 고장 진행 | 1,440 | 986 | 1.5배 |
문제가 있으면 절감이 줄어드는 것이 맞습니다 — 그때는 자료가 필요합니다.
학습은 서버가 하고, 현장은 계산만 합니다.
기준선에서 마할라노비스 모델을 만들어 내려보냅니다. 학습할 때는 이상치에 흔들리지 않는 계산이 필요하지만, 판정할 때는 그 결과 값만 있으면 됩니다. 그래서 현장 장치에 학습용 라이브러리를 넣지 않아도 됩니다.
| 대상 | 형식 | 크기 | 엣지에 필요한 것 |
|---|---|---|---|
| 게이트웨이 | JSON 파라미터 | 3.0 KB | numpy 몇 줄 |
| MCU | emlearn C 헤더 | 3.2 KB | C99 · 동적할당 없음 |
MCU용 C는 GaussianMixture(성분 1개·완전공분산)으로 표현합니다 —
수학적으로 마할라노비스와 같습니다. 실측으로 확인했습니다:
점수 상관 0.9844 · 판정 일치 100%.
LiteRT는 신경망 전용입니다. 우리 8종 중 변환되는 것은 오토인코더 하나인데, 실측에서 탐지율 40%로 가장 나쁩니다. 기준선이 설비당 20~30점인데 신경망은 그 정도로 학습이 안 됩니다.
원파형에서 직접 학습하는 CNN이 필요해지거나, 여러 설비 자료를 모아 사전학습한 모델을 쓸 단계가 되면 그때가 맞습니다. 배포 통로는 그대로 두고 모델만 갈아 끼우면 됩니다.
할 수 있습니다. 다만 저절로 돌아가게 두지는 않습니다.
마할라노비스는 충분통계만 있으면 옛 자료 없이도 정확히 갱신됩니다. 신경망처럼 전체 재학습이 필요 없습니다. 그런데 재봤더니, 계속 배우게 두면 고장을 늦게 찾습니다.
| 방식 | 오탐 | 발견까지 |
|---|---|---|
| 고정 (추가 학습 없음) | 1.8% | 62회 |
| 무조건 추가 학습 | 0.8% | 124회 |
| "정상일 때만" 학습 | 0.8% | 130회 |
| 정상일 때만 + 경보 후 멈춤 | 1.1% | 83회 |
"정상일 때만 배운다"는 안전해 보이지만 아닙니다. 고장 초기에는 아직 σ가 낮아 그 조건을 통과하고, 그때 배운 것이 이미 오염입니다. 모델이 고장을 따라가 버리면, 그 뒤로는 영영 이상해 보이지 않습니다. 그리고 베어링이 바로 천천히 나빠지는 쪽입니다.
그래서 이렇게 나눕니다.
confirm_normal 없이는 동작하지 않습니다. 그 구간이 정상이었는지는
데이터로 증명할 수 없으니, 사람이 밝히게 합니다.같은 실수를 되풀이하지 않으려고 적어 둡니다.
| 증상 | 원인 |
|---|---|
| 진단 API가 조용히 모든 점수를 0으로 냄 | 특징량 키 목록을 vib_*로 고정해 둬서, 다른 키를 주면 전부 0벡터가 됐다 |
| 게이트웨이가 3키만 전송 | 추출 함수가 내는 키 이름을 추측해서 매핑했다. 실제 이름과 달라 전부 비었다 |
MCU용 C에 std = 0.000000f |
회전수가 상수라 표준편차가 0이었다. C에서 0으로 나누면 그 결과가 화면에는 "이상"으로 보인다 |
| 같은 계획을 두 번 적용하니 화면이 두 벌 | 중복 방지를 에셋·디바이스에만 걸고 대시보드에는 안 걸었다 |
| 번역을 고쳤는데 화면은 그대로 | 번역 파일에 만료 캐시를 걸어 브라우저가 하루 동안 옛 파일을 썼다 |
| 고장을 주입해도 매번 보고가 나감 | 변화 판단을 비율로만 해서, 0.019와 0.022 같은 잡음이 15% 변화로 읽혔다 |