Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

103 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Neuro-Drive: 분산 UGV 제어 시스템 (RPi 5 + STM32)

C++ Python FreeRTOS STM32 Raspberry Pi

물리적으로 분리된 두 프로세서에 소프트 실시간(Linux)하드 실시간(RTOS) 역할을 나누고, 서로를 완전히 신뢰할 수 없는 상황에서 협력하게 만드는 방법을 보여주는 자율주행 RC카 플랫폼.

Ackermann 조향 UGV를 위한 이기종 MPU/MCU 아키텍처에서의 실시간 제어, 통신 파이프라인 설계, Fail-Safe 메커니즘에 초점을 맞춥니다.


시연 영상 — Phase 4: Return-to-Home

phase4_rth_demo.mp4

프로젝트 발전 과정

이 프로젝트는 6단계에 걸쳐 발전했으며, 각 단계는 이전 단계에서 드러난 문제를 해결하는 방식으로 진행되었습니다.
Phase 2와 3 사이의 전환점 — 필드 테스트 중 발생한 하드웨어 장애 — 은 전체 아키텍처를 재설계하는 계기가 되었습니다.
각 Phase를 클릭하면 상세 내용을 확인할 수 있습니다.

Phase 주제 해결한 핵심 문제 상태
1 단일 Linux 제어 단일 RPi 보드에서 Python ↔ C++ IPC 구현 ✅ 완료
2 STM32 분산 아키텍처 Linux의 Hard Real-Time 불가 → Brain(RPi) / Reflex(MCU)로 분리 ✅ 완료
⚠️ 전환점 — 운용 중 장애 발생 필드 테스트 중 7.4V 배터리 선이 RPi 3.3V GPIO에 접촉 → 전체 아키텍처 전면 재설계
3 RTOS 및 Interrupt 기반 제어 Bare-metal Polling의 100Hz 데드라인 미달 → FreeRTOS + ISR + Queue, P 제어 + Feedforward ✅ 완료
4 Return-to-Home 안전 시스템 Watchdog과 자율 동작의 충돌 → Keep-Alive 패턴, 8B → 12B 프로토콜 확장 ✅ 완료
5 OTA Firmware 업데이트 물리적 재플래싱 부담 → 커스텀 Bootloader, CRC 핸드셰이크, 섹터 관리 ✅ 완료
6 CAN Bus 및 Multi-ECU 단일 UART 병목 → 3노드 CAN 2.0 분산 제어 🔀 multi-mcu-can으로 이전
⚠️ 장애 — 조향 서보 소손 코드 수정 중 모터/서보 출력 매핑이 뒤바뀜 → 서보에 스로틀 값 인가, 스톨 소손 → 실차 검증 중단, CAN 작업은 벤치 환경으로 이전

⚠️ 장애 분석 — 조향 서보 소손 (발생 2026-05 · 원인 확정 2026-07)

F446RE 이식 자체는 성공하여 주행까지 확인했으나, 조종 반응 지연(큐 명령 적체)을 수정하는 과정에서 모터와 서보의 출력 타이머 매핑이 서로 뒤바뀌었습니다. 배선은 그대로였기 때문에 조향 서보가 스로틀 값을 위치 명령으로 받았고, 기계적 한계로 몰린 상태에서 스톨 전류가 지속되어 소손되었습니다. 같은 편집에서 비상정지 로직도 함께 뒤집혀, 안전장치가 오히려 작동할 수 없는 상태였습니다(공통원인고장).

당시에는 원인을 특정하지 못한 채 하드웨어 문제로 남겨두었고, 약 3개월 뒤 커밋 이력을 역추적하여 원인을 확정했습니다. 정상 주행 → 매핑 스왑 → 소손으로 이어지는 변경 사슬이 diff에 그대로 남아 있었습니다.

도출한 원칙

  • 핀 ↔ 장치 바인딩은 한 곳에서만 정의하고, 본문에서 타이머 핸들을 직접 사용하지 않습니다
  • 출력 경로를 변경한 커밋은 액추에이터를 분리한 상태에서 파형부터 검증합니다
  • 한 커밋에 성격이 다른 변경(로직 수정 + 매핑 정리)을 섞지 않습니다
  • 자동 생성 코드·설정 파일도 형상관리에 포함합니다 (tim.c 미포함으로 사후 분석이 제한되었습니다)

아키텍처

System Architecture

MPU (Raspberry Pi 5 / Linux) — Web UI, WebSocket 서버, UDP 릴레이, 상위 레벨 모드 관리 (RTH, Keep-Alive).
MCU (STM32F411RE / FreeRTOS) — UART ISR, RTOS 태스크 스케줄링, PWM 생성, 엔코더 읽기, 명령 타임아웃 감시(소프트웨어 워치독 태스크).

데이터 흐름

Browser ──WebSocket──▶ Python (Flask-SocketIO)
                          │
                          ├──UDP (12B: throttle, steering, mode)──▶ C++ Control Core
                          │                                              │
                          │                                         UART (115200)
                          │                                              │
                          │                                              ▼
                          │                                      STM32 (FreeRTOS)
                          │                                        ├─ UART RX ISR → Queue
                          │                                        ├─ Motor Task (PWM)
                          │                                        ├─ Encoder Task (TIM1)
                          │                                        └─ Safety Task (Watchdog)
                          │
                          └──UDP (Encoder telemetry)◀── C++ ◀──UART── STM32

MBSE 설계 다이어그램

IBM Rhapsody, StarUML을 사용해 요구사항 정의부터 구현까지 일관된 모델로 문서화했습니다.

  ▶ 클릭하여 펼치기     📐 MBSE 다이어그램 4종 — 유스케이스 · 클래스 · 시퀀스 · 상태차트

유스케이스 다이어그램

Use Case Diagram

Operator가 시스템과 상호작용하는 주요 유스케이스를 정의합니다. Control Vehicle Movement는 Cornering Boost 적용, Output Power 제한, Return-to-Home을 <<include>>하며, 하드웨어 환경(Hardware_Environment)이 Fail-Safe 실행에 참여합니다.

클래스 다이어그램 (C++ Control Core)

Class Diagram

RPi 측 Control Core의 C++ 클래스 구조입니다. SharedContextstd::mutexstd::atomic으로 보호되는 공유 상태를 소유하며, UdpReceiverVehicleController가 이를 참조합니다.

시퀀스 다이어그램 (제어 명령 흐름)

Sequence Diagram

조이스틱 입력이 Web UI → Python Middleware → C++ Core → STM32까지 전달되는 전 계층 데이터 흐름을 보여줍니다. WebSocket JSON → UDP Binary Packet → UART String 순으로 프로토콜이 변환됩니다.

상태차트 다이어그램 (RTH & Fail-Safe FSM)

State Chart Diagram

시스템의 동작 상태를 정의합니다. OPERATING 상태에서 RTH 명령 수신 시 RTH_RECORDINGRTH_ACTIVE로 전이하며, Watchdog 타임아웃(timeout == true) 발생 시 FAIL_SAFE로 전이합니다.


Phase 6 — CAN Bus 다이어그램

3노드 아키텍처와 배선 등 초기 설계는 이 저장소에서 수행했으며, CAN 통신 구현은 multi-mcu-can 에서 완료되었습니다.

  ▶ 클릭하여 펼치기     🔌 Phase 6 CAN 다이어그램 — 블록 · 시퀀스 · 배선도

블록 다이어그램 (3노드 CAN 아키텍처)

Phase 6 Block Diagram

RPi5(Gateway), STM32F446RE(MotorECU), STM32F411RE(SensorECU) 3노드가 MCP2515(SPI→CAN) + TJA1050(트랜시버)를 통해 CAN 2.0 버스로 연결됩니다. 단일 UART 병목을 해소하고 노드별 책임을 분리합니다.

시퀀스 다이어그램 (CAN 메시지 흐름 & 장애물 대응)

Phase 6 Sequence Diagram

0x100 MotorCMD(50ms), 0x200 MotorStatus(100ms), 0x300 SensorData(100ms, broadcast) 세 CAN ID로 노드 간 통신을 구성하도록 설계했습니다. SensorECU가 거리 20cm 미만을 감지하면 MotorECU가 자율적으로 PWM을 0으로 설정(자동 정지)하고, 상태를 Gateway를 거쳐 WebUI까지 전파합니다.

배선 다이어그램 (F446RE MotorECU — CAN 인터페이스)

F446RE CAN Wiring

STM32F446RE MotorECU CAN 인터페이스 핀 배선: bxCAN TX/RX → MCP2551 트랜시버 → 120Ω 종단 저항이 포함된 CAN 버스. 위 배선도는 직접 제작한 웹 기반 배선도 편집기로 그렸으며, 소스는 tools/CAN_F446RE.json입니다.


시연 영상

Phase 5 — OTA Firmware 업데이트

Video.Project.mp4

주요 기능

실패로부터 검증된 아키텍처 — 현재 설계는 Phase 2 필드 테스트 중 발생한 치명적 하드웨어 장애의 산물입니다. 탈락한 7.4V 배터리 선이 RPi의 3.3V GPIO에 접촉하는 사고 이후, 결정론적 C++ 제어 루프, RTOS 기반 Fail-Safe 로직, 물리적 절연이 도입되었습니다.

분산 제어 — RPi는 네트워킹과 모드 로직을, STM32는 Hard Real-Time 모터 제어를 담당합니다. 두 프로세서는 서로의 역할을 대체할 수 없습니다.

FreeRTOS 태스크 아키텍처 — UART 수신(ISR + Queue), 모터 제어, 엔코더 읽기, 안전 모니터링이 독립적인 RTOS 태스크로 실행되며, 태스크 간 명령 전달은 메시지 큐(osMessageQueue)로 처리합니다.

Feedback + Feedforward 하이브리드 제어 — STM32는 100Hz P-controller로 속도 오차를 보정(Feedback)하고, Python은 조향 각도에 비례한 코너링 부스트를 선적용(Feedforward)합니다. 조향은 open-loop 서보 제어입니다.

Watchdog & Fail-Safe — RPi(C++ 제어 루프)와 MCU(FreeRTOS Task_Safety)가 각각 500ms 명령 타임아웃을 독립 감시하는 2중 소프트웨어 워치독. 링크 두절 시 MCU가 RPi와 무관하게 모터 PWM 출력을 차단합니다 (Task_Safety 감시 주기 50ms → 최악 반응 지연 약 550ms).

Return-to-Home (RTH) — 엔코더 기반 경로 기록(LIFO 스택)과 자율 역주행. Watchdog 안전 기능과 자율 동작 간의 충돌을 Keep-Alive 패턴으로 해결하고, 전체 스택에 모드를 전파합니다. 복귀는 조작자 감독 하에서만 지속되며(dead-man switch), 복귀 중 링크 두절 시 즉시 FAIL_SAFE 정지합니다.

OTA Firmware 업데이트 — 핸드셰이크 프로토콜, CRC 검증 이미지 전송, 섹터 단위 Flash 관리, 올바른 Bootloader→Application 점프 시퀀스를 갖춘 커스텀 UART Bootloader.

MBSE 문서화 — IBM Rhapsody를 사용해 유스케이스, 객체 모델, 시퀀스, 상태차트 다이어그램으로 시스템 동작을 공식 문서화.


기술 스택

레이어 기술
Web UI HTML/CSS/JS, nipplejs (joystick), Socket.IO
서버 Python 3.11, Flask-SocketIO, eventlet
Control Core C++17, UDP socket, threads, mutex
Firmware C (STM32 HAL), FreeRTOS, UART ISR, TIM/PWM, 커스텀 Bootloader
통신 WebSocket, UDP (struct pack), UART (115200 baud)
설계 도구 IBM Rhapsody | StarUML (MBSE), STM32CubeIDE
하드웨어 Raspberry Pi 5, STM32 Nucleo-F411RE, L298N, Ackermann 섀시

하드웨어

전체 부품 목록은 docs/hardware.md에서 확인할 수 있습니다.

구성 부품
MPU Raspberry Pi 5 (4GB)
MCU — Main STM32 NUCLEO-F411RE (SensorECU, Phase 1~5)
MCU — Motor STM32 NUCLEO-F446RE (MotorECU, Phase 6)
CAN 인터페이스 (RPi5) MCP2515 + TJA1050 (SPI→CAN)
CAN 트랜시버 (F446RE) MCP2551 (bxCAN)
장애물 센서 HC-SR04P 초음파 센서
섀시 5KG Ackermann 프레임 (인코더 모터 내장)
모터 드라이버 Waveshare I2C Motor Driver HAT / L298N
배터리 LiPo 7.4V 2S (B2200N-SP35) + UBEC 5A 레귤레이터

실제 하드웨어 빌드 — RPi 5 + STM32 Nucleo 제어 스택, 전원 분배, 카메라/모터 배선


시작하기

사전 준비

  • Raspberry Pi 5
  • STM32 Nucleo-F411RE + STM32CubeIDE
  • Python 3.11

실행

# 1. STM32CubeIDE로 STM32 Firmware 빌드 및 플래시
#    (Bootloader 먼저, 이후 Application 이미지)

# 2. RPi에서 C++ Control Core 빌드
cd rpi/
g++ -o drive_server src/control_core_oop.cpp -pthread

# 3. 웹 서버 시작
cd rpi/web/
python3 app.py

# 4. 브라우저에서 접속 → http://<rpi-ip>:5000

OTA Firmware 업데이트 (Phase 5)

# 프로그래머 없이 UART로 새 Application 이미지 플래시
cd rpi/web/
python3 ota_flasher.py parkhaejin_car.bin

후속 프로젝트

multi-mcu-can — Phase 6은 CAN 2.0 Multi-MCU 분산 통신에 특화된 별도 저장소로 분리되었습니다. 액추에이터 레이어를 제거하고 핵심에 집중합니다: bxCAN 주변장치 설정, MCP2515 SPI-to-CAN 브리징, 다중 노드 간 메시지 중재.


작성자

박해진 (Haejin Park)

About

Distributed RC Car Control System — Raspberry Pi 5 (Master) & STM32F446RE/F411RE (FreeRTOS/C) with CAN multi-ECU, OTA bootloader, and Return-to-Home

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages