1. 수업 개요
이번 수업에서는 Embedded System의 기본 개념과 함께, Arduino 프레임워크와 M5Stack Core2를 중심으로 임베디드 개발의 전체적인 흐름을 살펴보았다.
이론에서는 임베디드 시스템이 어떤 구조로 이루어져 있는지, 그리고 실제 개발 시 어떤 구성 요소들이 사용되는지를 다뤘고, 실습에서는 M5Stack을 Arduino 환경에서 다루기 위한 기본적인 개발 환경을 설정했다.
2. Embedded System 기본 개념
Embedded System은 특정 목적을 수행하도록 설계된 전용 컴퓨팅 시스템이다. 일반적인 PC와 달리 제한된 자원 환경에서 하나의 명확한 역할을 수행하는 것이 특징이다.
임베디드 시스템은 하드웨어(MCU, 센서 등)와 소프트웨어(펌웨어)가 결합된 형태로 구성되며, 스마트워치, 가전제품, 자동차 제어 시스템, 로봇, 의료 기기 등 다양한 분야에서 사용된다.
3. 하드웨어 구성 요소 이해
임베디드 시스템의 핵심은 마이크로컨트롤러(MCU)이다. MCU는 하나의 칩 안에 CPU, 메모리, 입출력 제어 장치를 포함하고 있으며, 센서 데이터를 처리하고 외부 장치를 제어한다.
센서는 온도, 습도, 움직임, 빛과 같은 물리적·환경적 변화를 감지해 전기적 신호로 변환하는 역할을 한다. 이 데이터는 MCU로 전달되어 처리된다.
또한 디스플레이는 임베디드 시스템의 상태나 결과를 사용자에게 보여주는 출력 장치로, SPI나 I2C와 같은 통신 방식을 통해 MCU와 연결된다.
4. 통신 방식과 GPIO
GPIO는 MCU가 외부 장치와 직접 상호작용하기 위한 가장 기본적인 인터페이스이다. 입력 모드로 센서 값을 읽고, 출력 모드로 LED와 같은 장치를 제어할 수 있다.
수업에서는 임베디드 시스템에서 자주 사용되는 대표적인 통신 프로토콜도 함께 다뤘다.
- UART: 비동기 방식의 직렬 통신
- I2C: 두 개의 선으로 여러 장치를 연결하는 통신 방식
- SPI: 고속 데이터 전송에 적합한 동기식 통신
각 통신 방식은 연결 구조와 속도, 사용 목적이 다르며, 센서나 디스플레이 종류에 따라 적절한 방식을 선택해야 한다.
5. M5Stack Core2와 Arduino 플랫폼
M5Stack Core2는 ESP32 기반의 임베디드 개발 보드로, 디스플레이, 센서, 배터리, 무선 통신 기능이 하나의 모듈로 통합되어 있다.
빠른 프로토타이핑과 IoT 프로젝트에 적합하며, Arduino IDE를 통해 비교적 쉽게 개발할 수 있다.
Arduino는 하드웨어와 소프트웨어가 결합된 오픈소스 임베디드 플랫폼으로, 복잡한 설정 없이도 빠르게 코드를 작성하고 보드에 업로드할 수 있다는 장점이 있다.
6. Arduino 프로그램 구조
Arduino 프로그램은 두 개의 핵심 함수로 구성된다.
setup(): 프로그램 시작 시 한 번 실행되는 초기화 영역loop(): 프로그램이 실행되는 동안 반복적으로 수행되는 메인 로직
대표적인 예제로는 LED를 일정 주기로 켜고 끄는 Blink LED 코드가 있으며, 이를 통해 GPIO 제어와 프로그램 흐름을 이해할 수 있다.
7. 실습: M5Stack Arduino 환경 설정 및 출력
실습에서는 M5Stack 공식 Arduino 문서를 참고하여 M5Stack Core2를 Arduino 환경에서 실행해보는 과정을 진행했다.
초기 단계에서는 복잡한 센서 제어보다는, 디바이스가 정상적으로 동작하고 있는지 확인하는 것이 목적이었다.
- Arduino IDE 설치
- M5Stack 보드 패키지 추가
- 보드 및 포트 인식 확인
- M5Unified 라이브러리 기반 기본 코드 실행
아래 코드는 M5Unified 라이브러리를 사용해 M5Stack 디스플레이에 텍스트를 출력해본 간단한 예제이다.
#include <M5Unified.h>
void setup() {
// Initialize M5Stack with default settings
auto cfg = M5.config();
M5.begin(cfg);
// Clear the screen to black
M5.Display.fillScreen(BLACK);
// 1. Korea: White, Medium Size
M5.Display.setTextColor(WHITE);
M5.Display.setTextSize(2);
M5.Display.setCursor(10, 30);
M5.Display.print("Korea");
// 2. China: Red, Larger Font
M5.Display.setTextColor(RED);
M5.Display.setFont(&fonts::FreeSansBold12pt7b);
M5.Display.setCursor(10, 80);
M5.Display.print("China");
// 3. USA: Blue, Large Size
M5.Display.setTextColor(BLUE);
M5.Display.setTextSize(4);
M5.Display.setCursor(10, 150);
M5.Display.print("USA");
}
void loop() {
M5.update();
}
이 코드는 M5Stack의 디스플레이 기능을 활용해 서로 다른 색상과 크기의 텍스트를 화면에 출력한다.
코드에서 확인한 핵심 포인트는 다음과 같다.
M5.begin(cfg): M5Stack 하드웨어 초기화M5.Display: 디스플레이 제어 객체setTextColor(),setTextSize(): 텍스트 스타일 설정setCursor(): 출력 위치 지정print(): 실제 텍스트 출력
복잡한 로직은 없지만, 직접 코드를 업로드하고 디스플레이에 출력이 나타나는 것을 확인하면서 개발 환경이 정상적으로 구성되었음을 눈으로 확인할 수 있었다.
아래는 해당 코드를 실행했을 때 M5Stack 화면에 출력된 결과 화면이다.

위와 같이 기본적인 디스플레이 출력 이후에는, M5Stack에 내장된 IMU(Inertial Measurement Unit) 센서를 활용해 가속도와 각속도 데이터를 읽어보는 실습을 진행했다.
IMU는 가속도계(Accelerometer)와 자이로스코프(Gyroscope)를 포함하고 있으며, 기기의 움직임과 회전을 수치 데이터로 측정할 수 있다.
아래 코드는 M5Unified 라이브러리를 사용해 IMU 센서 데이터를 읽고, 이를 UART(Serial)를 통해 실시간으로 출력하는 예제이다.
#include <M5Unified.h>
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
Serial.begin(115200);
M5.Display.fillScreen(BLACK);
M5.Display.setTextDatum(middle_center);
M5.Display.setTextColor(GREEN);
M5.Display.drawString("Streaming IMU to UART...", 160, 120);
M5.Display.drawString("Baud: 115200", 160, 150);
}
void loop() {
M5.update();
float ax, ay, az; // Accelerometer (Gs)
float gx, gy, gz; // Gyroscope (deg/s)
M5.Imu.getAccel(&ax, &ay, &az);
M5.Imu.getGyro(&gx, &gy, &gz);
Serial.printf(
"%.4f,%.4f,%.4f,%.4f,%.4f,%.4f\n",
ax, ay, az, gx, gy, gz
);
delay(20); // 50Hz
}
시리얼 모니터에는 가속도(ax, ay, az)와 각속도(gx, gy, gz)가 CSV 형태로 연속 출력된다.
기기를 손으로 흔들거나 회전시키면, 출력되는 값이 즉각적으로 변화하는 것을 확인할 수 있었고, 센서가 실제 움직임에 반응하고 있음을 직관적으로 느낄 수 있었다.
출력 주기를 delay(20)으로 설정해 약 50Hz 주기로 데이터를 전송했으며, 이는 이후 데이터 시각화나 분석을 진행하기에도 무리가 없는 수준이다.

이제 아래는 짐벌락 현상에 대해서 설명하겠다.
IMU 센서 데이터를 UART로 실시간 출력하는 실습을 진행하면서, 자이로스코프 기반 자세 표현에서 발생하는 짐벌락(Gimbal Lock) 현상에 대한 설명도 함께 이루어졌다.
짐벌락은 3차원 회전을 Euler Angle(roll, pitch, yaw)로 표현할 때 발생하는 문제로, 특정 축이 겹치면서 자유도 하나가 사라지는 현상을 의미한다.
쉽게 말해, 세 개의 회전 축 중 두 개가 일직선상에 놓이게 되면 회전을 독립적으로 구분할 수 없게 된다. 이 상태에서는 실제로는 회전하고 있음에도, 센서 값이 비정상적으로 보이거나 예상과 다른 방향의 변화가 나타난다.
이번 실습 코드에서는 IMU로부터 가속도(ax, ay, az)와 각속도(gx, gy, gz)를 직접 읽어 UART로 스트리밍하고 있었다.
기기를 특정 방향으로 크게 기울이거나 한 축을 기준으로 회전시키면, 일부 축의 값이 급격히 변하거나 다른 축의 변화와 강하게 연동되어 나타나는 것을 확인할 수 있었다.
이는 센서가 고장난 것이 아니라, Euler Angle 기반 회전 표현의 구조적 한계로 인해 발생하는 현상이라는 점이 강조되었다.
이러한 문제 때문에, 실제 로봇, 드론, AR/VR, 게임 엔진 등에서는 Euler Angle 대신 Quaternion(쿼터니언)을 사용해 회전을 표현하는 경우가 많다.
쿼터니언은 4차원 벡터를 사용해 회전을 표현하며, 짐벌락이 발생하지 않고 연속적인 회전 계산에 유리하다는 장점이 있다.
아래는 짐벌락 현상 실습을 진행한 화면이다.

실습에서는 Euler Angle로 계산한 자세 데이터를 UART로 전송하고, Python으로 수신하여 짐벌락 현상을 확인했다. Pitch가 ±90도에 가까워지면 Roll과 Yaw 값이 급격히 변하는 것을 확인할 수 있었다.
Python 코드로 짐벌락을 감지하고, 쿼터니언을 사용하여 재계산하는 실습도 진행했다. 쿼터니언을 사용하면 짐벌락 없이 부드러운 자세 추정이 가능하다.
또한 M5Stack에서 쿼터니언 값을 직접 계산하여 UART로 전송하는 실습도 했다. 쿼터니언은 4개의 값(qw, qx, qy, qz)으로 표현되며, 이를 통해 짐벌락 문제를 완전히 해결할 수 있다는 것을 직접 확인했다.
8. 정리
이번 수업을 통해 Embedded System의 기본 구조와 Arduino 기반 임베디드 개발의 전반적인 흐름을 한 번에 정리할 수 있었다.
이론으로 개념을 이해한 뒤, 실제 M5Stack을 Arduino 환경에서 다뤄보며 앞으로 어떤 방식으로 실습이 진행될지 구체적으로 감을 잡을 수 있었다.
'UNLV' 카테고리의 다른 글
| [UNLV] - 분류 알고리즘과 TinyML(5일차) (3) | 2026.02.11 |
|---|---|
| [UNLV] - 드론 AI 실습: 텔로 드론(4일차) (0) | 2026.02.10 |
| [UNLV] - 머신러닝 기초와 IMU 센서 퓨전(3일차) (0) | 2026.02.07 |
| [UNLV] - 쿼터니언과 라즈베리파이: 실전 임베디드 개발 환경 구축(2일차) (0) | 2026.02.06 |
| [UNLV] - OT (0) | 2026.02.05 |