0%

Unity

손가락을 넣는 순간, 화면에 내 심박수가 숫자로 뜨고 그 아래에서 **P-QRS-T 심전도 파형**이 좌에서 우로 흘러가며 실시간으로 그려진다면 어떨까요?

전*원··수정됨 2026.07.21

손가락을 넣는 순간, 화면에 내 심박수가 숫자로 뜨고 그 아래에서 **P-QRS-T 심전도 파형**이 좌에서 우로 흘러가며 실시간으로 그려진다면 어떨까요?

심박수에 따라 파형 색이 바뀌어 서맥·정상·빈맥 상태를 직관적으로 알려주고, 손가락을 뗀 순간에도 10초간은 마지막 리듬이 화면에 머물러 안정감을 준다면요. 이번 포스팅에서는 Modbus RTU 프로토콜과 실시간 ECG 파형 합성을 결합한 인천 학생과학관 '심박수 테스트' 체험 콘텐츠의 기술적인 구현 과정을 공유합니다.

아키텍처_01_전체구조.png

1. 시스템 아키텍처 개요

본 프로젝트는 크게 세 가지 단계로 구성됩니다.

이중 시리얼 입력: 맥박 센서(Modbus RTU 9600bps, 디바이스 주소 0x20)와 손 감지 센서(Custom binary 0xFA + 상태)를 각각 다른 COM 포트에서 수신

데이터 정제: REG_HEART_RATE_SPO2 (0x0006)REG_TEMPERATURE (0x000A) 레지스터에서 심박수·산소포화도·체온을 읽어 10회 연속 0 허용 글리치 필터로 노이즈 제거

파형 렌더링: 정제된 BPM을 기반으로 Mathf.Sin 합성 P-QRS-T 파형을 600개 포인트 LineRenderer로 실시간 그리며, BPM 구간별로 색상·상태 텍스트를 자동 갱신

이 모든 과정은 Unity 2022 LTS의 단일 씬 구조에서 SerialManagerLineRenderer_HeartBeatTest 두 싱글톤이 이벤트로 협업하며 동작합니다.

2. 주요 기술적 구현

2.1 Modbus RTU + Custom Binary 이중 프로토콜 수신 (SerialManager_HeartBeatTest.cs)

맥박 센서와 손 감지 센서는 서로 다른 프로토콜을 사용하기 때문에, SerialManager는 두 개의 SerialPort 인스턴스를 병렬 운영합니다. 맥박 센서는 산업 표준인 Modbus RTU[주소 0x20, 함수 0x03, 레지스터, 길이, CRC16] 요청을 보내고 [0x20, 0x03, 바이트수, 데이터..., CRC] 응답을 받으며, 코드는 byte1 * 256 + byte2 방식으로 16비트 레지스터를 디코딩합니다. 한편 손 감지 센서는 가벼운 Custom Binary로 [0xFA, 상태바이트] 2바이트만 주고받아, 센서 특성에 맞는 프로토콜을 각각 선택함으로써 통합 복잡도와 반응 속도의 균형을 잡았습니다.

2.2 연속 0 허용 글리치 필터 (BPM 안정화 레이어)

센서는 손가락의 미세한 흔들림이나 압력 변화로 순간적인 0을 내보낼 수 있는데, 이를 그대로 화면에 반영하면 파형이 툭툭 끊어지며 체감이 나빠집니다. SerialManager는 최근 BPM 값을 Queue<int> recentBPMs에 10개까지 보관하고, 0이 들어올 때마다 zeroPacketCount를 증가시키되 *GLITCH_THRESHOLD_COUNT = 10 회 미만**일 동안은 이전 평균값을 유지합니다. 10회(약 10~11초)를 넘어서면 비로소 "진짜로 손을 뗀 상태"로 판정해 0을 반영하므로, 사용자의 순간적 미세 이탈에도 화면이 안정되게 유지됩니다.

2.3 수학적 P-QRS-T 파형 합성 (LineRenderer_HeartBeatTest.cs)

ECG 파형을 표현하는 가장 직관적인 방법은 녹화된 샘플을 재생하는 것이지만, BPM이 실시간으로 변하는 콘텐츠에서는 샘플 속도 조절이 부자연스럽습니다. 대신 한 주기phase 0 ~ 1)를 8개 구간으로 분할하여 각 구간에서 Mathf.Sin(t·π)에 진폭 계수를 곱해 P파·Q파·R파·S파·T파를 순차적으로 합성하는 방식을 택했습니다. cycleLength = 60 / bpm 공식으로 한 주기의 실제 시간을 계산하고, 매 프레임 ecgPhase += dt / cycleLength로 위상을 진행시키면 BPM이 올라갈수록 파형 주기도 자동으로 짧아지며 완전히 연속적인 곡선이 생성됩니다.

| phase 구간 | 의미 | 출력 Y |

| :---: | --- | --- |

| 0.00 ~ 0.08 | P파 | sin(t·π) × 0.12 |

| 0.15 ~ 0.19 | R파 피크 | sin(t·π) × 0.80 |

| 0.30 ~ 0.45 | T파 | sin(t·π) × 0.20 |

3. 사용자 경험(UX) 최적화

BPM 구간별 색상 피드백 : bpm < 60 서맥은 주황, 60~100 정상은 초록, > 100 빈맥은 빨강으로 파형 색을 자동 전환하고 "서맥/정상/빈맥" 한글 상태 텍스트를 병기하여, 수치를 읽지 않아도 한눈에 자신의 상태를 파악할 수 있도록 했습니다.

이중 센서 조합 판정 : 맥박 센서가 BPM을 보고하는 동안에도 별도 손 감지 센서가 독립적으로 isHandInserted 플래그를 유지해, 글리치 필터가 이전 BPM을 유지하는 10초 구간에서도 measuringText로 "측정 중인지 아닌지"를 정확히 구분해 표시합니다.

매끄러운 스크롤 파형 : LineRenderer의 600개 포인트를 매 프레임 좌측으로 한 칸씩 시프트하는 링 버퍼 방식으로 끊김 없는 좌→우 스크롤 효과를 구현했으며, displayDuration = 20초를 고정하여 사용자가 자신의 최근 20초 심박 리듬을 항상 한눈에 볼 수 있도록 했습니다.

자동 그래프 제어 : Panel 전환 시점에 StartGraph() / StopGraph()가 호출되어 측정 중이 아닐 때는 LineRenderer를 아예 비활성화함으로써, 시각적 잡음과 불필요한 CPU 연산을 동시에 제거했습니다.

4. 마치며

이 프로젝트는 산업용 Modbus RTU 프로토콜, 수학적 파형 합성, 그리고 실시간 렌더링을 한 씬 안에서 묶어내면서도 체험자에게는 "그냥 손가락을 끼우면 내 심장 박동이 보인다"는 직관적 경험으로 귀결시키는 작업이었습니다. 기술적으로는 CRC 검증, 글리치 필터, Sin 합성 파형 같은 요소가 흥미로웠지만, 교육 현장에서 학생이 자신의 심박을 눈으로 확인하며 "심전도는 이런 모양이구나"를 체감하게 했다는 점에서 큰 의미가 있었습니다.

향후에는 개인별 측정 이력을 로컬에 저장하여 시계열 비교 그래프를 제공하고, 실제 심전도 샘플을 딥러닝 모델에 학습시켜 이상 리듬(부정맥 패턴) 감지를 교육용으로 시각화하는 기능을 추가할 예정입니다.

읽기 도구

6분 읽기

이 글이 도움이 되었나요?

다음으로 읽기