0%

Unity

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

전*원··수정됨 2026.10.19

손가락을 넣는 순간, 화면에 내 심박수가 숫자로 뜨고 그 아래에서 **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의 단일 씬 구조에서 SerialManager와 LineRenderer_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분 읽기

이 글이 도움이 되었나요?

Work with GIWorks

프로젝트에 GIWorks의 경험이 필요하신가요?

전시·콘텐츠·엔지니어링 프로젝트의 아이디어와 고민을 들려주세요. 필요한 기술과 실행 방법을 함께 찾겠습니다.

문의하기

다음으로 읽기