brunch

공사장에서 드론을 이용해 사람을 구한다고?

공사장 실시간 관제 시스템 개발

by 휘드라

관제 시스템 개발

PoC형태로 구현한 드론 관제 시스템 및 동적 지오펜싱


저는 최근 건설 현장을 지나가다가 문득 이런 생각이 들었습니다. "저 거대한 굴삭기들이 움직이는 모습을 드론으로 실시간 모니터링할 수 있다면 얼마나 멋질까?" 그래서 집에와서 바로 관련 기술들을 리서치를 해보았습니다.


Dynamic Geofencing: 중장비 위치에 따라 실시간으로 위험 구역을 설정
1.png 출처 - Advancing Workplace Safety with Location Geofencing (National Safety Council)



많은 기술중에서 "동적 지오펜싱"이 인상 깊었습니다. 기존의 고정된 위험 구역이 아닌, 중장비가 움직일 때마다 실시간으로 위험 구역을 재설정하는 아이디어였거든요. 디지털트윈이라는 개념하에 드론을 통한 실시간 모니터링 시스템을 구현 해보면 어떨까 싶었습니다. 그래서 바로 시스템 흐름을 설계를 했습니다.




1. 시스템 흐름 설계


동적 지오펜싱을 생각하다보니 흥미로운 부분은 "디지털 트윈" 개념이었습니다. 실제 건설 현장을 가상공간에 그대로 복제하여, 현실과 동일한 가상환경에서 안전관리를 손쉽게 할 수도 있겠다 싶었습니다. 드론으로부터 실시간 영상을 전송받아 객체를 탐지하고, 이를 화면에 그리는 흐름은 아래와 같습니다.


2.png [드론 영상] → [RTSP 스트림] → [객체 탐지 AI] → [위험도 분석] → [실시간 알림]


이 구조의 핵심은 실시간성입니다. 건설 현장에서 몇 초의 지연이 생명과 직결될 수 있기 때문입니다.

백엔드: FastAPI (빠른 비동기 처리)

스트리밍: RTSP + FFmpeg + HLS

객체 탐지: YOLO

실시간 통신: WebSocket

프론트엔드: React + Leaflet (지도 라이브러리)




2. 실시간 스트리밍 (RTSP)

4.jpg

드론이 촬영한 영상이 어떻게 실시간으로 우리 화면에 도달하는지 궁금하지 않나요? 하늘 위의 드론 영상을 실시간으로 전송하기 위해서는 RTSP(Real-Time Streaming Protocol)를 이용해야 했습니다. RTSP를 쉽게 설명하면 "실시간 영상 전송을 위한 규칙"입니다. 마치 택배 배송에 정해진 규칙이 있듯이, 영상 데이터를 실시간으로 전송하기 위한 약속된 방식이죠.


하지만 아쉽게도 저에겐 실제 카메라 달린 드론이 없었습니다. 실제 드론 대신 가상 드론을 만들기 위해 MediaMTX라는 미디어 서버를 사용했습니다. 이는 RTSP 스트림을 관리해주는 일종의 "교통 관제소" 역할을 합니다. 저는 공사현장의 드론 촬영 영상(mp4) 파일을 구해서 실시간 가상 영상으로 활용했습니다.


# FFmpeg로 영상 파일을 RTSP 스트림으로 변환

ffmpeg -re -stream_loop -1 -i construction_site.mp4 -c copy -f rtsp rtsp://localhost:8554/drone_footage


-re: 실제 프레임 레이트로 재생 (영상을 실시간처럼 보내기)

-stream_loop -1: 무한 반복 (24시간 드론처럼)

-c copy: 인코딩 없이 그대로 복사 (빠른 처리)

-f rtsp: RTSP 형태로 출력




3. AI 객체탐지


자! 이제 실시간 스트리밍 구현이 완료되었으니 중장비를 탐지할 수 있는 AI 모델이 필요합니다. 중장비 객체 탐지를 위한 학습 데이터가 필요했는데, 이게 생각보다 쉽지 않더라고요.


첫번째 시도

처음에는 AIDCON(AI for Construction) 데이터셋을 사용하려고 했습니다. 건설 현장 특화 UAV 데이터셋으로 유명했거든요. 설레는 마음으로 데이터 주인분에게 데이터셋 신청서를 작성하고 기다렸는데...응답이 없더군요.


두번째 시도

직접 건설 현장을 돌아다니며 사진을 찍어볼까도 생각했습니다. 하지만 건설 현장은 보안이 엄격해서 함부로 들어갈 수도 없고, 드론을 띄우는 것도 여러 허가가 필요했습니다. 가능성이 희박한 접근 방법입니다.


세 번째 시도

마지막으로 Roboflow를 뒤져보았습니다. 관련 모델들을 API로 손쉽게 사용 가능했기 때문에 따로 데이터셋을 수집하고, 모델을 학습할 필요는 없었습니다. 중장비를 상공에서 수직으로 촬영한 UAV 사진 데이터 및 모델이 필요했는데, Roboflow를 뒤지고 뒤져서 construction-vehicle-detection-pxc7c라는 모델(YOLO)을 찾았습니다.


from roboflow import Roboflow


rf = Roboflow(api_key="your_api_key")

project = rf.workspace().project("construction-vehicle-detection-pxc7c")

model = project.version(2).model


6.png


4. 백엔드 서버 구현 (FastAPI)

FastAPI로 백엔드를 구현했습니다. 핵심은 RTSP 스트림을 받아서 객체 탐지를 수행하는 것이었어요. Spring, Node.js 등 여러 웹 프레임워크가 있지만, 이전 FastAPI 경험을 토대로 뚝딱뚝딱 구현하였습니다. 삽질을 방지해주신 킹갓제네럴 Cursor(클로드 Sonnet4)에게 감사와 존경의 말씀을 보냅니다. (__)


7.png


def detect_objects_in_frame(frame):

"""프레임에서 객체를 감지하고 결과를 로깅합니다."""

temp_image_path = "temp_frame.jpg"

cv2.imwrite(temp_image_path, frame)


# Roboflow로 객체 감지

result = model.predict(temp_image_path, confidence=40, overlap=30).json()


if result["predictions"]:

for prediction in result["predictions"]:

class_name = prediction["class"]

confidence = prediction["confidence"]

x, y = prediction["x"], prediction["y"]

print(f"감지된 중장비: {class_name} (신뢰도: {confidence:.2f})")


# WebSocket으로 실시간 전송

asyncio.run(manager.broadcast(json.dumps({

"type": "object_detection",

"class": class_name,

"confidence": confidence,

"position": {"x": x, "y": y}

})))



위에서 제작한 RTSP 스트림을 웹에서 확인할 수 있도록 HLS(HTTP Live Streaming)형태로 변환하였습니다. HLS를 쉽게 설명하면 "영상을 작은 조각으로 나누어서 전송하는 방법"입니다. 실시간 영상을 한번에 전송하면, 당연하게도 다운로드 시간이 오래걸리기 때문에 버퍼링과 같은 문제가 생깁니다. 이를 방지하기 위해 m3u8이라는 재생 리스트를 통해 순차적으로 다운로드 및 재생 하는 방식입니다. 이외에도 브라우저에 실시간 영상을 재생시켜야 하기 때문에 브라우저 호환성이 높은 HLS로 변환하였습니다.


@app.on_event("startup")

def start_hls():

cmd = [

"ffmpeg",

"-rtsp_transport", "tcp",

"-i", "rtsp://localhost:8554/drone_footage",

"-c", "copy",

"-f", "hls",

"-hls_time", "2",

"-hls_list_size", "3",

"-hls_flags", "delete_segments",

"public/stream.m3u8"

]


subprocess.Popen(cmd)


객체 탐지 결과를 실시간으로 프론트엔드에 전송하기 위해 WebSocket을 사용했습니다.


# WebSocket 연결 관리

class ConnectionManager:

def __init__(self):

self.active_connections: list[WebSocket] = []

async def broadcast(self, message: str):

for connection in self.active_connections:

try:

await connection.send_text(message)

except:

pass


@app.websocket("/api/ws")

async def websocket_endpoint(websocket: WebSocket):

await manager.connect(websocket)

try:

while True:

data = await websocket.receive_text()

await manager.send_personal_message(f"Received: {data}", websocket)

except WebSocketDisconnect:

manager.disconnect(websocket)




5. 프론트엔드 (React + Leaflet)

프론트엔드 개발을 시작할 때 가장 고민했던 부분이 "어떻게 실시간으로 건설 현장을 시각화할 것인가"였습니다. 단순한 영상 스트리밍만으로는 약간 재미가 없었습니다. 그래서 저는 중장비의 위치와 위험 구역을 직관적으로 보여줄 수 있도록 몇가지 기능 및 UI/UX를 추가하였습니다. 처음에는 React 컴포넌트로 Leaflet 지도를 관리하는 구조를 만들었고, WebSocket을 통해 백엔드에서 전송하는 객체 탐지 결과를 실시간으로 영상에 반영했습니다.


디지털 트윈 느낌을 주기 위해서 실시간 영상 외에도 2D 아이콘 구현하였다.


그리고 가장 핵심적인 동적 지오펜싱을 프론트엔드에서 구현하였습니다. 탐지된 객체(굴삭기, 덤프트럭 등) 에 대해서 위험 반경을 그려주는 코드를 대충 작성했습니다.


const getDangerRadius = (vehicleType) => {

const radiusMap = {

excavator: 15, // 굴삭기: 15m 위험 반경

dump_truck: 20, // 덤프트럭: 20m 위험 반경

crane: 30 // 크레인: 30m 위험 반경

};

return radiusMap[vehicleType] || 15;

};


객체탐지 후 지오펜싱을 구현하였다.


어쩌다 크레인으로 인식한다. AI 모델 성능이 그리 좋지 않다.


최종 구현된 UI는 다음과 같은 구성요소를 포함하고 있습니다.

메인 지도: 실시간 중장비 위치와 위험 구역 표시

영상 스트림: HLS로 전송되는 드론 영상

알림 패널: 위험 상황 실시간 알림

통계 대시보드: 중장비 수, 위험 구역 수 등

설정 패널: 위험 반경, 알림 설정 등


6. 후기

종종 아이디어를 실제로 구현해보는 것은 너무 흥미롭습니다. 여러 기술스택을 빠르게 경험해볼 수 있었습니다. 물론 실제 현업에서 사용하려면 더 정밀하고 빠르게 만들어야 합니다.


이 PoC를 통해 느낀 점은 "기술은 결국 사람을 위한 것"이라는 겁니다. 복잡한 AI 모델과 실시간 스트리밍 기술을 사용해서 건설 현장에서의 안전을 도모할 수 있다는 것은 너무나 매력적인 일 같습니다.


그리고 생각보다 많은 도구들이 이미 준비되어 있답니다. 코딩분야에서 AI의 발전 속도는 너무나도 놀랍습니다. 그리고 Roboflow 같은 플랫폼 덕분에 개인 개발자도 충분히 의미 있는 시스템을 만들 수 있는 시대가 도래했습니다. 여러분도 일상에서 마주치는 문제들을 기술로 해결해보시는 건 어떨까요?

keyword
매거진의 이전글위성사진 건축물 탐지