ArduPilot SITL이 잡아주는 것과, 더는 진실을 말해주지 않는 지점
이 글은 영어판도 있습니다.
노트북에서 잡은 버그는 오후 반나절이면 끝납니다. 같은 버그를 공중에서 잡으면 기체 한 대가 날아갑니다. ArduPilot SITL이 실제로 증명해주는 게 무엇이고, 시뮬레이션이 멀쩡히 통과해도 아무 의미가 없는 지점이 어디인지 정리했습니다.
SITL은 펌웨어를 흉내 내지 않는다, 그대로 돌린다
SITL을 굳이 세팅할 값어치가 여기 있는데, 대개 그냥 지나칩니다.
Software In The Loop은 비행 컨트롤러를 흉내 낸 모델을 돌리는 게 아닙니다. 진짜 ArduPilot 소스를 sitl 타겟으로 컴파일해서 네이티브 프로세스로 띄웁니다. 같은 스케줄러, 같은 EKF, 같은 모드 전환, 같은 페일세이프 로직이 데스크톱에서 돕니다. 바뀌는 건 물리와 센서 드라이버뿐이고, 검증하려는 코드는 그대로입니다.
그래서 SITL에서 모드 전환이 이상하게 굴면 시뮬레이터 탓이 아니라 그냥 버그입니다.
환경만 한 번 잡아두면 기체 띄우는 데 명령 하나면 됩니다.
git clone --recurse-submodules https://github.com/ArduPilot/ardupilot
cd ardupilot
./Tools/environment_install/install-prereqs-ubuntu.sh -y
./Tools/autotest/sim_vehicle.py -v ArduCopter --console --map
첫 실행은 waf로 펌웨어를 빌드하느라 몇 분 걸리고, 그다음부터는 몇 초면 뜹니다. MAVProxy 콘솔과 지도가 열리고, 기본 위치에 시동 걸 준비가 된 멀티콥터 한 대가 놓입니다.
컴패니언 컴퓨터가 쓸 그 경로로 붙는다
MAVProxy는 편하지만 여기가 핵심은 아닙니다. 중요한 건 컴패니언 컴퓨터가, 제 경우엔 Jetson인데, 실제 기체에서 쓸 바로 그 인터페이스로 가상 기체와 이야기하게 만드는 겁니다.
SITL은 비행 컨트롤러의 시리얼 포트를 TCP 소켓으로 열어줍니다.
| 포트 | 대응 | 주 용도 |
|---|---|---|
| 5760 | SERIAL0 | 콘솔, MAVProxy가 붙는 곳 |
| 5762 | SERIAL1 | 컴패니언 컴퓨터 |
| 5763 | SERIAL2 | 두 번째 링크 |
pymavlink든 MAVSDK든 직접 짠 MAVLink 코드든 tcp:127.0.0.1:5762에 붙이면, 나중에 UART로 주고받을 그 프로토콜을 그대로 씁니다. SITL 상대로 짠 오프보드 제어 코드가 그대로 비행에 올라갑니다.
-I 1을 주면 포트 블록이 10씩 밀려서 인스턴스 1은 5770번대를 씁니다. 노트북 한 대로 군집 비행을 굴려보는 방법입니다.
일부러 고장을 낸다
SITL의 진짜 쓸모는 여기입니다. 기체가 미션 도는 걸 구경하려는 게 아닙니다. 미션은 어차피 잘 돕니다. 하필 가장 곤란한 순간에 뭔가 터졌을 때 어떻게 반응하는지를 보려는 겁니다.
ArduPilot에는 실행 중에 고장을 주입하는 SIM_* 파라미터가 있습니다. 기체가 날고 있는 상태에서 MAVProxy 콘솔로 바로 바꿉니다.
param set SIM_WIND_SPD 12
param set SIM_WIND_DIR 270
param set SIM_RC_FAIL 1
바람, GPS 소실, RC 페일세이프, 배터리 전압 강하, 개별 모터 고장, 기압계 드롭아웃. 전부 비행 중에 켜고 끌 수 있습니다. 고장 처리 코드는 정상 테스트로는 한 번도 실행되지 않으면서, 정작 기체를 되찾아 오느냐를 결정합니다.
파라미터 이름은 본인 빌드에서 확인하세요.
SIM_*파라미터는 ArduPilot 릴리스마다 바뀝니다. 특히 GPS 시뮬레이션 쪽은 한 번 크게 정리돼서, 오래된 튜토리얼은 지금 없는 이름을 가리킵니다. 블로그 글을 믿지 마시고, 이 글도 마찬가지고, 실제로 돌리는 버전에서param show SIM_*를 쳐보세요.
실시간보다 빠르게, 화면 없이 돌린다
SITL은 펌웨어를 물리 모델과 락스텝(lockstep)으로 돌립니다. 기체의 시계가 벽시계가 아니라 시뮬레이션을 따라 흘러간다는 뜻입니다. 그래서 전체를 빨리 감아도 코드가 보는 타이밍은 망가지지 않습니다.
./Tools/autotest/sim_vehicle.py -v ArduCopter --speedup 10 --no-mavproxy
20분짜리 미션이 2분에 끝납니다. MAVProxy를 떼면 화면 없이 돌아가는데, CI에 넣을 때 필요한 형태가 이겁니다. ArduPilot 자체 테스트 스위트도 이렇게 돕니다.
./Tools/autotest/autotest.py
제어 로직이 정해둔 시나리오를 사람 없이 통과하기 시작하면, 비행 스택 회귀 테스트도 그냥 평범한 소프트웨어 작업이 됩니다.
SITL이 더는 진실을 말해주지 않는 곳
위에 쓴 것 전부보다 이 대목이 중요합니다. 시뮬레이션이 통과했다는 사실은 과하게 읽히기 쉽습니다.
1. 센서가 너무 얌전합니다. 실제 기체의 IMU는 프레임 공진, 모터 하모닉, 컨트롤러를 잘못 얹어서 생기는 에일리어싱을 다 봅니다. SITL의 기본 노이즈 모델은 이 중 어느 것도 재현하지 않습니다. 노치 필터는 시뮬레이션에서 튜닝할 수 없고, 비행을 망치는 가장 흔한 원인인 진동 문제는 여기서 아예 안 보입니다.
2. 타이밍이 데스크톱 타이밍입니다. 프로세스 하나 돌리는 노트북 코어는 센서와 로깅과 텔레메트리를 다 지고 있는 H7이 아닙니다. 실제 하드웨어에 부하가 걸릴 때 나오는 스케줄러 오버런이 SITL에서는 끝까지 안 나올 수 있습니다.
3. EKF가 너무 깨끗한 데이터를 받습니다. 건물 옆 GPS 다중경로, 컴퍼스 가까이 지나간 전원선의 자기 간섭, 햇빛 받으면 드리프트하는 기압계. 실제로 기체를 떨어뜨리는 건 이런 것들인데 기본 모델에는 없습니다. GPS가 끊기는 상황은 주입할 수 있습니다. 그럴듯하게 틀린 GPS 해를 주입하는 건 훨씬 어렵습니다.
4. 아무것도 헐거워지지 않습니다. 진동으로 커넥터가 빠지는 일도, ESC가 디싱크되는 일도, 프로펠러 밸런스가 어긋나는 일도, 그날 세 번째 비행에서 납땜이 갈라지는 일도 없습니다.
정직하게 말하면 이렇습니다. SITL은 로직을 증명합니다. 기체를 증명하지는 않습니다. 시뮬레이션이 깨끗하게 돌았다는 건 비행 허가증이 아니라 줄 매달고 호버링 한번 해봐도 좋다는 뜻입니다. 대신 얻는 게 있습니다. 들판에 나갈 때쯤이면 남아 있는 버그가 물리적인 것들뿐입니다. 그리고 그건 책상에서는 어차피 찾을 수 없는 종류입니다.
정리
- SITL은 진짜 ArduPilot 펌웨어를 돌립니다. 여기서 나온 버그는 진짜 버그입니다.
- 컴패니언 컴퓨터를
tcp:127.0.0.1:5762에 붙여서, 비행에 쓸 그 MAVLink 인터페이스로 오프보드 제어를 개발하세요. SIM_*파라미터로 바람, GPS 소실, RC 페일세이프, 모터 고장을 실행 중에 주입합니다. 이름은 본인 빌드에서 확인하세요.--speedup과--no-mavproxy를 쓰면 비행 스택 회귀 테스트도 평범한 CI 작업이 됩니다.- 진동, 실제 하드웨어 타이밍, 지저분한 센서 데이터, 기계적 고장은 전부 모델 밖입니다. 통과했다고 그 이상으로 읽지 마세요.
SITL에 딸린 물리 모델은 제어 로직을 검증하기에는 충분합니다. 인지 스택에 실제로 보여줄 것이 필요해지는 순간부터는 부족하고, 그때부터 외부 시뮬레이터가 필요합니다. 다음 글에서 다루겠습니다.
Comments
Post a Comment