본 프로젝트는 Raspberry Pi 5, PiCamera2, 그리고 Arduino Uno를 기반으로 한 WebRTC 실시간 영상 스트리밍 및 라인트레이싱 자율주행 RC카를 구현하는 것을 목표로 합니다.
PiCamera2가 캡처한 실시간 영상을OpenCV로 처리하여 라인을 인식하고, 중심 좌표를 계산합니다.- 해당 좌표는 시리얼 통신을 통해 Arduino에 전달되며, Arduino는 이를 바탕으로 모터(ESC)와 서보를 정밀하게 제어하여 선을 따라 주행합니다.
- RC 조종기(CH1, CH2, CH6)를 통해 수동 제어 또한 가능하며, CH6 스위치로 자율/수동 모드를 전환할 수 있습니다.
- 향후 초음파 센서, LIDAR 등 다양한 확장을 고려한 모듈형 설계가 적용되었습니다.
| 이름 | 역할 및 기여 내용 |
|---|---|
| 윤창혁 | 시스템 전체 설계 및 통합 PiCamera2 영상처리 알고리즘 구현 Arduino PWM 제어 알고리즘 구현 자율주행 모드에서의 오프셋 기반 조향 알고리즘 최적화 |
| 심창완 | WebRTC 서버 및 웹 인터페이스 구현 라인 이탈 복원/대응 알고리즘 개발 실시간 웹소켓 통신 설계 및 연결 안정성 확보 aiortc 기반 WebRTC 서버 최적화 및 지연 시간 감소 전략 적용 |
.
├── arduino/
│ └── hybrid_manual_auto.ino # 수동 및 자율 주행 통합 아두이노 코드
├── README.md # 본 문서
├── LICENSE # 오픈소스 라이선스 (MIT)
└── WebRTC/
│ ├── camera_stream_server.py # python WebRTC 서버 코드
│ └── index.html # WebRTC 웹 뷰어 코드
└── images/
└── linetracking.png # 웹 뷰어 이미지
[RC 조종기 (CH1/CH2/CH6)] [PiCamera2 + Raspberry Pi]
│ │
└─ PWM 입력 ──┐ ┌────┘
▼ ▼
[Arduino Uno] ◀── Serial 통신 ─── [Line Detection + Offset 계산]
│ │ │
Servo ESC(PWM) (Python + OpenCV)
▼ ▼ │
[RC Car] 자율/수동 주행 ▼
[📄 HTML 뷰어 (index.html)]
│
브라우저에서 WebRTC + WebSocket 수신
- PiCamera2 + Raspberry Pi: HSV 마스크 기반 라인트레이싱 및 영상 스트리밍
- Arduino Uno: PWM 입력 디코딩 및 시리얼 수신 기반 서보/ESC 제어
- WebRTC 서버:
aiortc,websockets기반 실시간 영상 송출 - LED 피드백: 조향 방향 및 속도 상태를 시각적으로 표시
- 입력 해상도:
640x480 (RGB888) - 처리 파이프라인:
- HSV 색공간 변환
- 검은색 마스킹 및 Morphological Closing
- ROI1 (하단) →
cv2.fitLine()기반 중심 좌표 계산 - ROI2 (상단) → 보조 예측 정보 추출
- 시리얼 포맷:
center_x,center_y\n - 미검출 시
-1,-1\n전송, 100ms 이상 간격 유지
- CH1: 조향 (Steering)
- CH2: 속도 (Throttle)
- CH6: 모드 전환
- 시리얼 수신 중심 좌표 기반 offset 계산
- offset → 비선형 보정(steerExpo) → steerRange 반영
- Y 좌표에 따라 속도 전환 (
cornerSpeed↔straightSpeed) - 미검출 시 곡선 후진 로직 수행
- PWM 직접 디코딩 및 steerExpo 보정
- 전후진 속도 계수 반영
- 좌우 조향: 좌/우 LED 점멸
- 후진 시: 양쪽 LED 점등
본 시스템은 Raspberry Pi (Python) 에서 영상 처리된 라인트레이싱 결과를 Arduino (C++) 로 전송하여 자율주행 동작을 수행합니다.
단방향 Serial 통신(RPi → Arduino) 기반으로 구현되며, 통신 구조는 단순하지만 실시간성과 안정성을 동시에 제공합니다.
| 항목 | 설명 |
|---|---|
| 통신 방향 | Raspberry Pi → Arduino (단방향) |
| 통신 속도 | 9600 bps |
| 전송 주기 | 최소 100ms 이상 (버퍼 오버플로 방지 목적) |
| 데이터 포맷 | "center_x,center_y\n" |
| 정상 예시 | 320,410\n |
| 미검출 예시 | -1,-1\n |
| 에러 처리 | 100ms 이상 수신 없음 또는 -1,-1 수신 시 감지 실패로 간주 |
# 중심 좌표가 감지되었을 경우
serial.write(f"{cx},{cy}\n".encode())
# 라인을 찾지 못했을 경우
serial.write("-1,-1\n".encode())cx,cy: 라인 중심 좌표 (정수형)\n: 각 프레임의 종료를 나타내는 종단 문자- 전송 간격: 최소 100ms 이상
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
int commaIndex = serialBuffer.indexOf(',');
if (commaIndex > 0) {
lineCenter = serialBuffer.substring(0, commaIndex).toInt();
lineY = serialBuffer.substring(commaIndex + 1).toInt();
lastSerialTime = millis();
lostLineCount = 0;
}
serialBuffer = "";
} else {
serialBuffer += c;
}
}- 문자 단위로 읽기 →
\n수신 시 패킷 완성 ,를 기준으로 좌표 분리 →toInt()로 변환- 정상 좌표 수신 시
lastSerialTime초기화 및 로직 실행 - 비정상 또는 누락 시 감지 실패 처리
if (millis() - lastSerialTime > serialFrameTimeout) {
if (lostLineCount < lostLineThreshold) {
lostLineCount++;
lastSerialTime = millis();
}
if (lostLineCount >= lostLineThreshold) {
lineCenter = -1;
lineY = -1; // 감지 실패로 간주
}
}- 일정 시간 수신 없음 시:
lostLineCount증가 - 연속 실패(
lostLineThreshold초과) →-1,-1로 강제 처리 - 이 상태에서는 후진 또는 회피 등 안전 로직 실행 가능
| 항목 | 내용 |
|---|---|
| 전송 포맷 | "center_x,center_y\n" (UTF-8 인코딩) |
| 전송 주기 | 100ms 이상 |
| 수신 방식 | 문자 단위 파싱, 종단문자 \n 기준 |
| 에러 처리 | -1,-1 전송 또는 timeout 발생 시 감지 실패 간주 |
-
프로젝트 클론
git clone https://github.com/YUNCHANGHYEOK/RC_Project.git cd RC_Project -
가상환경 생성 및 활성화
가상환경 생성 및 활성화 python3 -m venv venv source venv/bin/activate # Windows의 경우: venv\\Scripts\\activate
-
필요한 패키지 설치
pip install opencv-python aiortc websockets picamera2 numpy
-
WebRTC 영상 처리 서버 실행
source venv/bin/activate # 가상환경 활성화 python camera_stream_server.py # 라인트레이싱 + WebRTC 서버 실행
- Picamera2로 라인을 인식하고
- 라인의 중심좌표를 아두이노로 전송
- WebRTC + WebSocket을 통해 브라우저로 영상 스트리밍
-
클라이언트 HTML 뷰어 실행
- 다른 터미널에서 동일한 가상환경을 활성화한 후, HTML 파일이 있는 디렉토리에서 아래 명령어 실행:
source venv/bin/activate # 가상환경 활성화 cd html/ # (예: HTML 파일이 html 폴더에 있다면) python -m http.server 8000 # 웹서버 실행
- HTML 내부에 포함된 JavaScript가 WebSocket (8765)을 통해 라인 중심 좌표와 영상을 수신
| 초기 버전 1 | 초기 버전 2 | 초기 버전 3 |
|---|---|---|
![]() |
![]() |
![]() |
| 최종 버전 |
|---|
![]() |
- 이미지에는 두 개의 초록색 사각형이 보이며, 이는 각각 ROI1 (하단)과 ROI2 (중간 상단)으로 선 추적을 위한 분석 구간입니다.
- 노란 점은 ROI1 내에서 검정 선의 중심을 계산한 좌표로, 차량 제어에 활용됩니다.
- 빨간 점은 ROI2 내 중심으로, 주로 선의 진행 방향 예측이나 조향 판단에 참고됩니다.
- 각 ROI의 픽셀 데이터를 기반으로 직선을 추정하고, 중심 좌표를 아두이노 및 WebSocket으로 전송하여 제어 시스템과 연동됩니다.
-
🎯 HSV 마스크 기반 라인 검출
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_black = np.array([0, 0, 0]) upper_black = np.array([180, 90, 170]) mask = cv2.inRange(hsv, lower_black, upper_black)
- 카메라 영상에서 어두운(검정색) 선을 인식하기 위해 BGR 이미지를 HSV로 변환한 후, 특정 범위에 해당하는 픽셀만 흰색으로 마스킹
-
🟩 ROI1에서 중심 좌표 계산(라인 추적)
roi = mask[320:400, :] # 하단 영역 잘라내기 ys, xs = np.where(roi == 255) if len(xs) > 100: points = np.array(list(zip(xs, ys))) [vx, vy, x0, y0] = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) t = ((400 - 320) // 2 - y0) / vy cx = int(x0 + vx * t)
- 영상 하단 영역(ROI1)에서 흰색 픽셀 위치를 수집한 뒤, 직선을 피팅하여 라인의 중심좌표를 계산
- 이 값은 RC카의 주행 중심에 활용
-
🔁 이전 중심 유지 로직(라인 미탐지 대비)
if len(xs) <= 100: lost_counter += 1 if last_valid_center and lost_counter < 10: center_x, center_y = last_valid_center else: center_x = center_y = None
- 라인이 일시적으로 감지되지 않더라도, 최근 탐지된 좌표를 일정 프레임 동안 유지시켜 RC카가 부드럽게 주행하도록 함
-
🔌 아두이노로 좌표 전송
if center_x is not None: arduino.write(f\"{center_x},{center_y}\\n\".encode()) response = arduino.readline().decode().strip()
- 감지한 중심 좌표를 아두이노로 시리얼 통신을 통해 전송
- RC카의 조향 및 ESC 제어는 이 값을 기반으로 처리됨
-
📡 WebRTC를 통한 영상 스트리밍
video_frame = VideoFrame.from_ndarray(color_frame, format=\"bgr24\") video_frame.pts = pts return video_frame
- 처리된 프레임을 WebRTC용 비디오 프레임 객체로 변환한 뒤, 브라우저로 전송
- 브라우저에서는 실시간으로 라인 검출 상태 확인 가능
-
Arduino IDE 설치
-
라이브러리 설치
PinChangeInterrupt(스케치 > 라이브러리 관리에서 설치)
-
업로드 절차
hybrid_manual_auto.ino열기- 보드: Arduino Uno / 포트 설정
- 컴파일 후 업로드
- 라인 인식 예시
🚗 일직선 자율주행
|
🧪 시범 자율주행
|
✅ 실제 구현 완료된 자율주행
|
- Raspberry Pi 5
- PiCamera2
- Arduino Uno
- RC 수신기 + 조종기
- 서보 모터, ESC
- Python 3.9+
- OpenCV, aiortc, websockets
- Arduino IDE 1.8+
- PID 조향 제어: 곡선 주행 성능 향상 (실제 주행때 한번 트랙에서 벗어났다 돌아오는 현상 발생 -> 이를 해결하기 위해 조절)
- 실시간 Web UI: 브라우저 상에서 주행 정보 확인 및 수동 조작 기능
- 적응형 라인 색상 감지: HSV 범위 자동 조정 기능 추가 (빛번짐에도 라인을 잘 디텍싱할 수 있도록 조절)
- 다중 ROI 보정: 선 단절 구간 예측 대응 향상
- 센서 통합 확장: 초음파, LIDAR 등 외부 센서 연동 (앞에 장애물이 있다면 이를 인식하고 조절할 수 있도록 발전)
- 성능 최적화 및 속도 증가: 자원의 효율 개선(속도를 더욱 늘려서 빠른 주행이 가능하도록 조절)
이번 WebRTC 기반 라인트레이싱 자율주행 RC카 프로젝트를 진행하면서 다양한 기술적 도전과 성장을 경험할 수 있었습니다.
특히 하드웨어와 소프트웨어가 유기적으로 결합된 시스템을 설계하고 구현하는 과정에서 많은 것을 배웠습니다.
저는 시스템 전체 설계 및 통합, 그리고 PiCamera2 영상처리 알고리즘 구현에 집중했습니다.
특히 라인트레이싱 시 오프셋 기반 조향 알고리즘을 최적화하는 과정에서 많은 시행착오를 겪었지만,
끊임없는 테스트와 디버깅을 통해 RC카가 안정적으로 라인을 따라 주행하는 모습을 보며 큰 보람을 느꼈습니다.
실제로 하루 종일 코드를 수정하면서, 프로그래머에게 있어 개발뿐만 아니라 테스트와 디버깅 과정이 얼마나 중요한지를 깊이 깨달았습니다.
이 과정은 단순한 기능 구현을 넘어서, 하드웨어와 소프트웨어 간의 섬세한 인터페이스 설계가 프로젝트의 완성도에 큰 영향을 미친다는 것을 알게 해주었습니다.
또한 이번 프로젝트를 통해, 기술적인 구현만큼이나 README 문서 작성과 기록 정리의 중요성도 새롭게 실감할 수 있었습니다.
명확한 문서화는 유지보수나 협업에 필수적이며, 프로젝트를 더욱 가치 있게 만드는 핵심 요소임을 다시금 깨닫는 계기가 되었습니다.
WebRTC 서버와 웹 인터페이스 구현을 담당하며,
실시간 영상 스트리밍의 복잡성과 웹소켓 통신의 안정성 확보에 대해 깊이 있게 이해할 수 있었습니다.
특히 aiortc 기반의 WebRTC 서버를 최적화하여 지연 시간을 최소화하는 과정은 도전적이었지만,
사용자에게 끊김 없는 실시간 영상을 제공할 수 있게 되어 뿌듯합니다.
또한 라인 이탈 시 복원/대응 알고리즘을 개발하면서
예측 불가능한 상황에 대한 시스템의 견고성을 높이는 데 기여할 수 있었습니다.
이번 프로젝트를 통해 팀원들과의 긴밀한 협업의 중요성을 다시 한번 느꼈습니다.
각자의 역할에 최선을 다하면서도 서로의 어려움을 함께 고민하고 해결해 나가는 과정이 있었기에
성공적으로 프로젝트를 마무리할 수 있었습니다.
앞으로도 이러한 경험을 바탕으로 더욱 복잡하고 흥미로운 프로젝트에 도전하고 싶습니다.
MIT License — LICENSE 파일 참조



