[Embedded Challenge] 자율주행 프로젝트 회고

먼저 풀어야 할 문제를 명확하게 정의해보자.
미로는 약 5 m × 10 m 직사각형 공간이며, 출발선에서 출발하여 북쪽에 위치한탈출구에 충돌 없이 가장 빠르게 도달하는 것이 목표다.
장애물은 uBrain 포장 박스로 구성된 고정형 장애물과 이동형
uBrain 1 대로 구분된다. 여기서 핵심은 탈출구가 반드시
북쪽에 위치하며 ‘ㅁ’형 맵이 아니라는 특징 그리고, 탈출구에
도달하기 위해 필요한 방향 전환 횟수가 정해져 있다는
부분이다.
이 강한 가정을 통해서, 완전성이 보장되므로 이 문제를 디바이스에 방향에 대한 정보를 제공하여 북쪽(디바이스가 처음 놓인 방향) 탈출구로 안정적으로 수렴시키는 그리디 문제로 재정의했다.
초기에는 Occupancy Grid 기반 mapping, 벽 추출, A*/D* 전역 경로계획을 통합하는 풀스택 자율주행 파이프라인을 구현 및 실측했다.
그러나 실측 과정에서 우리 팀이 받은 로봇의 좌·우 모터 출력 비대칭이 상대적으로 타 팀의 로봇보다 크고, 디바이스 내에 IMU 센서 부재로 인한 다소 부정확한 상태 추정을 수행할 수 밖에 없다는 근본적 한계를 확인했다.
이러한 부정확한 환경 내에서 자세 추정의 누적 오차로 인해 Occupancy Grid 매핑 시 오히려 주행 안정성이 떨어지는 문제가 발생하였다. 따라서, 당일 오전에 맵이 공개된다는, 즉 우리가 미로의 구조를 알 수 있다는 점에 착안하여 2D 기반의 맵을 하드코딩하고, 추정하기 어려운 동적 장애물의 구간을 y 좌표의 임계점을 개략적으로 정의하여 확률 모델링을 통해 디바이스에게 동적 장애물에 대한 정보를 전달하여 추정한다는 원칙으로 설계를 축약했다.
이론적 최적성보다는 하드웨어 제약하에 견고성에 대한 핵심 원칙을 투영할 수 있는 알고리즘이 임베디드 시스템에서 더 중요하다고 생각했다.
〈최종 아키텍처〉
하드코딩한 미로(고정 벽) + EKF 센서 퓨전 + Greedy Cardinal 내비게이션 + 결승 직전 동적 장애물 확률 모델링
미로에는 곡선 구조가 전혀 없다. 모든 구성이 직교 구조로 구성된다. 이 구조를 고려해보면 로봇의 주행을 상하좌우 + 90 도 코너 회전으로 단순화 할 수 있다.
단순한 구조를 바탕으로 로봇의 주행을 복잡한 경로 추종 로직 대신 Finite State Machine 으로 환원한다.

다만, 이 Finite State Machine 이 제대로 작동하기 위해서는 직진에 강하게 수렴하는 주행과 90 도 회전의 정밀도가 매우 중요하다.
회전이 조금 어긋나거나 직진이 조금 어긋나는 경우 그 오차가 누적되고 경로가 무너지기에 회전·직진 보정을 집중적으로 검토했다.
1. 주행 알고리즘 - Greedy 북향 내비게이션 (Cardinal FSM)
Non-wall-following 과 함께, 측벽 거리의 변화량 Δ𝑑를 기준으로 코너 및 교차로를 이산 감지하여 회전하며 통로를 통과하되, 항상 북쪽(도착지)을 선호하도록 설정한다.
구체적으로, 진행방향에 우선순위를 부여한다(북:0, 서·동:1, 남:2). 전면 충돌이 임박한 EMERGENCY 를 포함한 90 도 회전 상황에서는 이 우선순위에 따라 회피 방향을 결정하며(priority-prefer rotation), 동일 우선순위가 충돌할 경우 좌·우 센서 값을 비교해 더 넓은 쪽으로 진행한다.
이러면 충돌 직전 상황에서도 전체 진행 방향이 목표로 수렴한다.
또한 교차로 판별 시 북향 진행이 가능하면 북쪽을 강제 선택한다. 이러한 규칙을 통해, 북향 단조 구조를 갖는 미로라는 가정 하에서 사전 전역 경로 없이도 출구로의 도달이 보장된다.
절대 좌표가 아니라 측벽 거리 및 cardinal heading(상대 방향)에 기반해 분기를 판단하므로, 위치 추정 누적 오차 드리프트에 강하고 부정확한 측위 위에서도 강하게 동작한다.
2. 주행 알고리즘 - 동적 장애물 확률 모델링
이동형 uBrain 1 대는 위치가 변하니 하드코딩 맵으로 다룰 수 없고, IMU·정밀 센서가 없는 본 보드에서 이동체를 정밀 추적하는 것은 오히려 불안정하다. 따라서 정밀 추적 대신 관측-후-결단 휴리스틱을 채택했다.
1. 결승점 직전 마지막 구간(실습 채점기준 4 구간)에 진입하면 동적 회피 모드를 활성화한다.
2. 센서 빔 종점에 점유 확률을 시간 가중으로 누적 및 감쇠시켜 정적 벽과 이동 물체를 분리한다.
3. 그 상태에서 로봇은 N 회 북향 사이클을 수행하며 가능한 만큼 안전하게 전진하고 이동 장애물의 통과 리듬 및 간격을 기록한다.
4. 전면이 임계 거리 이상으로 열린 통과 창이 확인되면, 순항 속도를 일시적으로 높여(V_cruise → V_dash) 장애물이 되돌아오기 전에 빠르게 통과한다.
if ( front_clear ≥ D_pass AND cycle_count ≥ N ) ⇒ v ← V_dash
이동체의 정확한 위치·속도를 추정하려면 고품질 센서·필터가 필요하지만, 통과 가능한 시점만 포착하는 문제로 환원하면 저품질 센서로도 충분하다.
확률 감쇠 덕분에 한 번의 순간적 감지가 통로를 차단하지 않으며, N 회 관측은 우연한 오감지에 의한 무모한 돌진을 막는 안전장치로 작동한다.
3. 주행 알고리즘 - 교차로 판정 + 정지시간 보정 함수
측벽 개구는 측면 초음파 센서로 판정하는데, 저성능 센서임을 고려하여, 아래와 같이 노이즈에 버틸 수 있도록, 누적 + 감쇠를 구현한다.
즉, 단발 스파이크가 아닌 일정시간 지속적으로 열려있는지를 확인하도록 증거를 누적하고 시간이 지나면 leak 하는 구조로 만든다.

이로 인해 연속적으로 여러 Tick 이 열려야 임계를 넘으므로, 오판이 억제된다.
더 나아가서 한 번 정지 또는 판단한 교차로를 즉시 재 트리거 하지 않는 debounce 효과 또한 부수적으로 얻을 수 있다.
판정 즉시 회전하면 교차로 중앙이 아닌 진입부에서 돌아 벽에 충돌한다.
따라서 개구 판정 후 기존 방향으로 일정 시간 더 진행해 중앙에 정렬한 뒤 회전해야 한다.
이 진행 시간을 단일 상수로 하드코딩하면 주행 속도에 따라 실제 진행 거리가 달라져 회전 위치가 흔들린다.
특히 직전 정지 직후에는 로봇이 가속 구간에 있어 속도가 낮고, 시간이 지나면 순항 속도에 수렴한다.
이를 보상하기 위해, 직전 motor_stop() 이후 경과 시간 Δt 를 현재 속도의 대리 변수로 삼아 진행 시간 t_advance 를 다음과 같이 정의했다(값은 실측 캘리브레이션 결과를 썼음).

위 두 수식을 통해서 교차로 판단의 안정성과 반응성을 동시에 확보한다.
4. 오류 보정 - Calibration
시간을 가장 많이 투자한 영역이다. 이상적 알고리즘이 현실 하드웨어에서 그대로 동작하지 않는 물리 오차를 추적하고 보정하는 부분이 매우 중요했다.
1) 배터리 의존성
동일한 코드인데도 배터리 충전 상태에 따라 모터 거동이 달라졌다. 충전이 낮으면 회전수가 떨어지고 그 전하량도 좌측 모터와 우측 모터가 달라, 동일한 듀티비에도 한쪽 바퀴가 덜 돌아 직진이 휘고 90 도 회전이 어긋났다.
즉 TICKS_PER_CM, pivot substep 같은 캘리브레이션 상수가 배터리 상태에 따라 달라진다.
그러니 항상 100% 충전된 상태를 가정한 후 Calibration 했고, 실전에서는 100% 충전 상태를 유지했으며 회전을 인코더 폐루프로 구현해
자가보정이 어느정도 가능하도록 설정했다.
2) 바퀴에 엉킨 머리카락

90 도 회전 및 직진 Calibration 도중 좌측 우측 인코더 값이 비정상적으로 크게 달랐다.
코드 수준에서 몇 번을 돌려봐도 나아지지 않았다.
하드웨어를 직접 점검한 결과 우측 바퀴 축에 상대적으로 더 많은 머리카락이 엉켜 회전을 물리적으로 방해하고 있었다.
이를 제거한 뒤 다시 Calibration 하자 좌우 대칭성이 크게 완화되었다.
좌측 사진은 바퀴에 붙어있던 머리카락의 일부로, 좌측보다 우측에 훨씬 더 많은 양의 머리카락이 붙어있음을 확인할 수 있다.
3) 전용 Calibration 모드
CALIB_PIVOT, CALIB_IR, CALIB_ODOM 을 빌드 스위치로 분리해 90 도 회전, IR 센서 탐색 수준, 직진 정도를 반복해서 측정했다.
이후 알고리즘 적인 측면에서 보정 전략은 아래와 같다.
Wheel Odometry - RK2(midpoint) 적분으로 EXTI 인코더 ISR 이 quadrature edge 마다 좌·우 휠 카운터를 증감하여 uint16 wrap 을 signed delta 캐스팅으로 보정하여 한 tick 에 |Δ| < 2¹⁵ 범위까지 안전하다. 좌·우 휠 변위로부터 본체 변위 Δs 와 회전 Δθ 를 계산한다.
EKF 센서 퓨전 (Predict + Update) 으로 상태벡터는 x = [x, y, θ]ᵀ, 공분산 P ∈ ℝ³ˣ³. X·Y 는 모터 인코더 적분, θ 는 센서값 기반으로 보정되는 구조로, 인코더의 누적 드리프트를 초음파 센서가 잡아준다.
최종 주행 알고리즘 흐름도는 아래 참고

태스크 구성 및 설계는 FreeRTOS API를 사용함.
CS를 전공하고 관련된 프로젝트를 진행하면서 항상 소프트웨어만 다뤄왔다.
대부분의 버그는 코드 내에서 해결할 수 있고 거의 모든 경우에 동일한 입력에는 동일한 출력을 확인할 수 있었음.
다만 하드웨어를 다룰 때는 디버깅 포인트가 늘어난다. 코드를 제대로 작성했더라도 현실세계에서 발생하는 다른 물리적인 변수에서 유발되는 여러 문제점이 있음.
프로젝트 하면서 바퀴에 엉킨 머리카락, 모터성능, 배터리 풀충 등 현실세계에서는 고려할 부분이 더 많다.
그러니 디버깅을 코드 수준에서만 하지 말고 하드웨어 수준도 고려해야 한다.
가설을 세우고 검증하는게 중요해보임. 데이터과학이랑 비슷한 느낌..
Calibration이 진짜 정말 중요했다.
보고서 원문 참고
'💬 기록' 카테고리의 다른 글
| NET 챌린지 : 멀티드론 기반 3D 상황인식 시스템 - 착수보고회 (0) | 2026.07.23 |
|---|---|
| [Data Science] BirdCLEF+ 2026 챌린지 결과 발표 (0) | 2026.06.10 |
| [Data Science] ETRI 휴먼이해 인공지능 논문경진대회 챌린지 결과 발표 (0) | 2026.06.10 |
| [StackUp] 종합설계 Sprint 2·3 Backlog 및 계획 발표 (0) | 2026.06.01 |
| [StackUp] 종합설계 Sprint 1 Backlog 및 계획 발표 (0) | 2026.05.10 |
댓글
이 글 공유하기
다른 글
-
NET 챌린지 : 멀티드론 기반 3D 상황인식 시스템 - 착수보고회
NET 챌린지 : 멀티드론 기반 3D 상황인식 시스템 - 착수보고회
2026.07.23 -
[Data Science] BirdCLEF+ 2026 챌린지 결과 발표
[Data Science] BirdCLEF+ 2026 챌린지 결과 발표
2026.06.10 -
[Data Science] ETRI 휴먼이해 인공지능 논문경진대회 챌린지 결과 발표
[Data Science] ETRI 휴먼이해 인공지능 논문경진대회 챌린지 결과 발표
2026.06.10 -
[StackUp] 종합설계 Sprint 2·3 Backlog 및 계획 발표
[StackUp] 종합설계 Sprint 2·3 Backlog 및 계획 발표
2026.06.01