본문 바로가기

어디부터 읽을까요?

드론·제어

제어 원리에서 ROS 2·PX4 비행 구현까지 연결합니다.

로봇·Physical AI

로봇이 보고, 배우고, 행동하는 데 필요한 개념을 읽습니다.

실용 개발

바로 적용할 작은 예제로 개발 중 막힌 문제를 해결합니다.

학습 기록 2편 — 항공 원리
Robotics/Control Theory

ROS2 × PX4로 시작하는 자율비행 드론 제어 (12-2) 처음부터 다시 실행하는 자율비행 프로젝트 — 연재 마무리

반응형
반응형

세 줄 요약

  • MPPI 계산부터 Gazebo 제어까지 대표 실험을 다시 실행하며 연재를 마무리합니다.
  • 원본 상태와 명령, FCU 수신 로그를 연결하고 새 ZIP 추출본에서도 같은 검증을 수행합니다.
  • 독자는 코드와 설정의 역할을 구별하고 실패를 포함한 재현 결과를 해석할 수 있습니다.

처음부터 프로젝트를 다시 실행했을 때 어느 단계까지 확인할 수 있을까요? episode_12_2.py로 계산·폐루프·실제 ROS 연결과 Gazebo 대표 과제를 다시 실행했습니다. 9-2 자체 모델 폐루프 12조건 중 6조건이 추종 기준을 통과했으며, 실제 로그로 계산한 호버링·이동 과제의 전체 위치 RMSE는 0.1364m였습니다. 9-1의 독립 계산은 예측까지만 확인한 별도 단계이며, 이번 재현에서는 과제 실패와 증거 검증 실패를 구별합니다.

 

그림 1. 이번 실행의 자체 모델 원본 CSV와 실제 Odometry의 시각에 맞춘 위치 오차, 외부 계산 시간을 보여 드립니다. 초록 음영은 코드에 미리 정한 평가 구간입니다. 자체 모델과 Gazebo 결과는 서로 다른 패널로 구분하며 기준의 참조 생성 시간은 PX4 내부 계산 시간이 아닙니다.

이전 12-1 비교 평가 /130은 세 반복의 제한적인 비교였습니다. 이번에는 같은 핵심 소스의 대표 조건을 다시 실행하고, 전체 목차 /83의 앞 장과 어떤 증거로 이어지는지 정리합니다. 기본 설치나 Offboard 이륙 설명을 반복하기보다 결과 폴더를 읽는 방법에 집중합니다.

1. 프로젝트는 상태에서 명령과 응답으로 연결됩니다

상태는 [x,y,z,vx,vy,vz,roll,pitch,yaw,p,q,r]입니다. 위치·선속도는 ENU 세계 좌표[m, m/s], 자세는 FLU 몸체에서 ENU로 향하는 ZYX 각[rad], p·q·r은 FLU 몸체 각속도[rad/s]입니다. 몸체 각속도를 Euler 각의 변화율로 그대로 적분하지 않습니다.

MPPI가 반환하는 [c,p_sp,q_sp,r_sp]는 정규화 추력[무차원]과 몸체 각속도 목표[rad/s]입니다. controller.py가 예측·샘플링·비용·갱신을 담당하고, ros_interface.py가 실제 Odometry를 같은 계약으로 바꿉니다. mask128 AttitudeTarget에는 FLU rate를 그대로 넣습니다. MAVROS가 FRD로 한 번 변환하며 PX4는 내부 rate 제어·제어 배분·모터 출력을 계속 맡습니다.

7편의 SI 추력[N]·토크[N m]·모터 회전속도[rad/s]와 이 목표 입력은 같은 물리량이 아닙니다. rigid_body_reference.py는 SI 수식 점검용이고 실제 연결에는 검증된 정규화/rate 모델을 사용합니다. 옛 모터 예제의 배분 rank는 3, 수정 참조는 4였습니다. 보존된 원문을 100조건으로 실행해 회전행렬은 맞고, 몸체 rate의 Euler 적분과 누락된 관성 교차항을 고쳐야 한다는 근거를 다시 얻었습니다. 옛 코드를 그대로 비행에 넣거나 토크의 이름만 rate로 바꾸지 않습니다.

추력 관계 T/m=9.81*c/c_hover와 0.08초 rate 시간상수는 근사 가정입니다. 매 실제 비행의 정지 호버 명령으로 c_hover를 초기화하지만 추력 곡선이나 PX4 전체 응답을 식별한 것은 아닙니다.

2. 이번 실행 범위와 환경을 고정합니다

정확한 PX4 commit은 ba365725710c35f76b05116ac95cf6c39cf0e4d4이며 실행 바이너리 SHA-256은 0981bc0747b66c3cf7ca4e6a6b0c468d54f97c30d96eebdf8550847f8d7d0c5c입니다. Docker 이미지 ID는 sha256:82bd42f8d4c81d40979935e955cb1670b687fe3521e9824ec56eecc8f0fb7f14입니다. 저장소의 dirty diff와 실제 실행 바이너리는 별도로 보존했습니다. 현재 소스 트리에서 이 바이너리를 새로 빌드했다고 주장하지 않습니다.

gz-sim8-cli 8.12.0-1~noble
ros-jazzy-mavros    2.14.0-1noble.20260126.183901
ros-jazzy-mavros-msgs   2.14.0-1noble.20260126.175124
ros-jazzy-ros-base  0.11.0-1noble.20260126.203129
Python packages: {"numpy": "1.26.4", "matplotlib": "3.6.3", "pyulog": "1.2.0", "pymavlink": "2.4.47"}

기존 Ubuntu 24.04·ROS 2 Jazzy 환경, MAVROS·MCAP·Docker·빌드된 PX4/Gazebo 작업공간이 필요합니다. ZIP에는 이번 애플리케이션 소스와 설정, 앞 장의 원고·감사 자료가 들어 있습니다. 운영체제·Docker 이미지·모든 과거 대용량 로그가 함께 들어 있는 배포 이미지는 아닙니다. 정확한 world/model/init/plugin 자산과 바이너리는 실제 trial의 provenance/runtime_assets.tar.gz, 버전·diff는 provenance/manifest.json에 남습니다.

실제 Gazebo 서버는 컨테이너의 Harmonic 8.11.0을 사용했습니다. 위 목록의 8.12.0은 호스트 설치 버전입니다. 모델 계산에 사용한 Python은 3.12.3입니다. 재현에는 colcon과 ros2cli, rclpy·ROS 메시지 패키지, rosbag2_py와 MCAP 저장 플러그인이 필요합니다. 장애물의 위치·접촉 관측에는 호스트 Python에서 불러올 수 있는 gz.transport13, gz.msgs10과 google.protobuf(protobuf)가 필요합니다. 이 바인딩과 colcon의 세부 버전은 제공된 버전 목록에 없으므로 임의의 버전을 실행 버전으로 쓰지 않습니다.

이번 순서는 독립 계산, 자체 모델 12조건, 비무장 실제 인터페이스, 호버링·이동·복귀, H20 시간 궤적, 약한/팽창 장애물 비용, seed7 기준/MPPI 한 쌍입니다. sample500·dt0.05초·온도3·기존 노이즈와 비용을 실제 비행에 유지합니다. 자체 모델은 원래 Config와 온도1·3·10, 시드7·11·23을 사용합니다. 실제 물리 기체 비행은 없습니다.

3. 새 폴더에서 같은 진입점을 실행합니다

ZIP을 빈 폴더에 푼 뒤 다음 명령을 실행합니다. --workspace-root는 외부 PX4 런타임과 이전 근거가 있는 기존 프로젝트를 가리킵니다. PYTHONPATH의 맨 앞은 방금 푼 코드로 두므로 저장소의 다른 애플리케이션 코드로 바뀌지 않습니다.

아래 경로는 모두 --workspace-root를 기준으로 준비해야 합니다. 감사 함수는 ZIP 안의 보존 사본을 입력으로 대신 읽지 않고 기존 작업공간에서 다음 자료를 읽습니다.

  • 지침 6개: AGENTS.md, docs/DRONE_MPPI_SERIES_HANDOFF.md, publishing/MPPI_SERIES_DESIGN.md, publishing/MPPI_EDITORIAL_POLICY.md, docs/BLOG_EDITORIAL_POLICY.md, publishing/GIF_POLICY.md입니다.
  • 회차 등록부: publishing/artifacts/mppi_daily/registry.json입니다. 9-1~12-1의 글 ID와 기록된 공개 상태를 확인합니다. 9-1 상태 파일은 publishing/artifacts/mppi_series/publication_9_1/state.json, 9-2~12-1은 publishing/artifacts/mppi_daily/episodes/<회차>/state.json입니다.
  • 과거 실행 자료: 9-1은 publishing/artifacts/mppi_series/9_1/20261002T080546_781565Z/, 나머지는 각 상태 파일의 evidence_run이 가리키는 원래 디렉토리입니다. 그 안의 result.json, source/ 소스 사본, 원고, CSV·NPZ와 해당 비행의 MCAP·ULog·JSONL·첨부가 필요합니다. 과거 결과의 trials[].output에 기록된 절대 경로도 읽을 수 있어야 합니다. 이번에 확인한 정확한 원본 경로 목록은 series_audit.json의 evidence_root, raw_evidence, publication_assets에 있습니다. 실행 폴더에 원고가 없으면 publishing/drafts/series_9_1_controller.md 또는 publishing/drafts/series_9_2_closed_loop.md를 읽습니다.
  • 1~8편 보존 원문: publishing/artifacts/blog_remodel/20261007/articles/<글ID>.txt입니다. 대상 ID는 83·85·84·86·87·88·89·90·92·93입니다. 해당 파일이 없을 때는 publishing/artifacts/series_9_1_revision/access_recheck/public_<글ID>.txt를 찾습니다.
  • 외부 실행 자산: third_party/px4_sitl/PX4-Autopilot/의 Git 정보와 그 아래의 build/px4_sitl_default/bin/px4, build/px4_sitl_default/etc/, build/px4_sitl_default/src/modules/simulation/gz_plugins/, Tools/simulation/gz/models/, Tools/simulation/gz/worlds/입니다. 실행 환경 기록을 위해 기존 작업공간의 simulation/scripts/와 simulation/config/도 읽습니다.

실행 계정에는 이 입력 파일의 읽기 권한과 상위 디렉토리 접근 권한, PX4 바이너리 실행 권한, Docker 데몬 사용 권한이 필요합니다. 새 추출 폴더와 출력 폴더에는 빌드·로그·새 ZIP 추출본을 쓸 권한이 있어야 하며, 기존 작업공간의 simulation/experiment_logs/.lock은 열고 잠글 수 있어야 합니다. 잠금 디렉토리가 없으면 생성 권한도 필요합니다. 컨테이너에는 작업공간을 읽기 전용으로, 해당 실행의 runtime/을 실행 계정의 UID/GID로 쓰기 가능하게 연결합니다. ZIP에 담긴 prior_evidence/와 series_audit.json만 복사해 두어도 이러한 감사 입력 경로와 권한이 대체되지는 않습니다.

source /opt/ros/jazzy/setup.bash
export PYTHONPATH="$PWD/simulation:$PYTHONPATH"
/usr/bin/python3 -m mppi_series.episode_12_2 \
  --workspace-root /home/sg/stack/src \
  --output-root "$PWD/experiment_logs"

마지막 표준 출력은 {"output":"절대경로/UTC_고유폴더", "passed":true} 형태입니다. output은 결과 폴더의 절대 경로이고, passed는 실행·증거 검증의 완료 여부입니다. 이 경로의 result.json에서 완료 여부와 재현 범위(scope=reproduction)를 확인합니다. 예시는 출력 형식이며 실패하면 passed=false가 기록됩니다. 파일 생성이나 프로세스 종료만으로 비행 성공을 판단하지 않습니다.

실행기는 기존 SITL lock과 포트를 확인하고 소유한 instance7·system8·ROS domain77의 서버만 사용합니다. offboard_control은 해당 실행 폴더에서 빌드하고 실제 설치 launch를 거칩니다. MPPI 비행 평가 중에는 PX4 위치 입력을 보내지 않습니다. PX4 위치 제어를 쓴 이륙·호버 초기화와 AUTO.LAND는 평가에서 제외합니다. 기준 비교 비행은 별도 서버에서 위치 제어로 실행하며 MPPI 적용 비행이라고 표시하지 않습니다.

기본 실행은 새 ZIP을 또 다른 폴더에 풀어 같은 진입점을 다시 실행합니다. --reproduce-child는 이 재귀 단계만 생략하는 내부 재현 옵션이며 실제 실험과 원본 저장은 모두 수행합니다. 새 ZIP 추출본에서도 동일한 비무장 연결 1회와 실제 비행 6회, 9-1 독립 계산 및 9-2 자체 모델 폐루프 12조건을 다시 실행했습니다.

4. 증거가 이어져야 적용 비행입니다

trials/이름/commands.csv에는 원본 Odometry 시각, 계산 상태, 추력·rate 목표, 실제 명령 시각과 계산 시간이 있습니다. rosbag/*.mcap의 실제 Odometry와 AttitudeTarget를 대조하고, 같은 시드·상태·미래 참조로 MPPI를 결정적으로 재생합니다. ULog에서는 무장·공중·rate-Offboard, 활성 rate 제어, 비활성 위치·자세 제어와 실제 받은 rate/thrust를 확인합니다. 관측한 timesync offset으로 ROS와 FCU 시각을 맞춘 값 검사는 ±100ms 후보 중 성분 오차5e-5 미만이 95% 이상이어야 합니다. 이 검사는 일대일 전송 지연 측정이 아닙니다.

10-1 비무장 재현에서 실제 계산·메시지 349개를 확인했고 FCU 일치율은 100.00%였습니다. 상태를 의도적으로 오래되게 만드는 검사를 실행해 명령 차단과 PX4 stream-loss 기록을 확인했습니다. 이는 연결/차단 검증이며 비무장 상태의 위치 차이를 비행 추종 성능으로 부르지 않습니다.

이번 10-2의 고유 Odometry 시각별 고정 목표 평가 RMSE는 전체 0.1364m, 각 10초 구간 마지막 4초는 0.0349m, 전체 구간 최대 오차는 0.5125m였습니다. 원래 10-2의 판정은 계산 시점 상태 기준으로 각 목표 구간의 마지막 위치 오차 <0.35m와 마지막 4초 RMSE <0.4m를 함께 확인합니다. aligned_point_measurements.csv에서 독립적으로 계산한 이번 숫자는 그 판정 수치와 구별하며, 고정 구간 최대 오차는 이 요약에 따로 제시하지 않습니다.

대표 과제 고정 구간 RMSE[m]·평가 범위 최대 오차[m]·범위 사전 판정 기준 판정
원 궤적 추종 (11_1_long_horizon/long_horizon) 0.0422 0.0975·전체 구간 고정 [7,27)초 위치 RMSE <0.12m 통과
약한 장애물 비용 (11_2_weak_cost/weak_cost) 0.2531·불완전 구간 178표본, 실제 평가 길이 12.90초 0.3841·중단 전 전체 확보 구간 실제 접촉 0, 최소 보수적 이격거리 ≥0.10m, 최종 목표 오차 <0.25m, 30초 과제 완료 실패
팽창 장애물 비용 (11_2_inflated_cost/inflated_cost) 0.4598 0.9081·전체 구간 실제 접촉 0, 최소 보수적 이격거리 ≥0.10m, 최종 목표 오차 <0.25m, 30초 과제 완료 통과
PX4 위치 제어 비교 (12_1_seed7_px4_position/12_1_seed7_px4_position) 0.1464 0.1858·고정 [7,27)초 구간 고정 [7,27)초 위치 RMSE <0.12m 실패
MPPI 비교 (12_1_seed7_mppi/12_1_seed7_mppi) 0.0331 0.0580·고정 [7,27)초 구간 고정 [7,27)초 위치 RMSE <0.12m 통과

표의 고정 구간은 참조 시작 뒤 [7,27)초입니다. 궤적과 비교 과제는 이 구간의 위치 RMSE로 판정하지만, 장애물 과제는 접촉·이격거리·도착·과제 완료를 함께 판정합니다. 따라서 장애물 행의 RMSE는 직선 참조에서 벗어난 정도를 보여 주는 보조 수치이며 0.12m 기준을 적용하지 않습니다.

weak_cost의 실제 평가 길이 12.90초는 중단 전 전체 확보 구간의 길이입니다. 0.2531m는 그중 [7,27)초에 들어오는 178개 표본만으로 계산했으며, 완전한 고정 구간의 RMSE가 아닙니다. 분석에는 부분 비행(partial_flight=true)과 고정 구간 미완료(fixed_window_complete=false)가 남습니다. 나머지 궤적·장애물 행의 전체 구간은 인계 후 [0,30)초의 확보된 측정값을 뜻합니다. 궤적·장애물의 고정 구간 최대값은 이 요약에 따로 제시하지 않았고, 비교 두 행의 전체 최대값은 각각 표시한 고정 최대값과 같았습니다. 시간 궤적은 현재 목표 한 점 대신 예측 지평선의 위치·속도 참조를 사용하는지 실제 predicted_references.jsonl로 검증합니다.

5. 실패도 다시 실행해 남깁니다

 

그림 2. 이번 두 장애물 trial의 실제 Gazebo pose 원본으로 그린 경로와 기체 여유 거리입니다. 원은 물리 원통과 중심이 피해야 하는 팽창 반경을 구분합니다. 음수 보수적 여유 거리는 포락 구의 겹침이며 실제 접촉 여부는 원본 contact 기록에서 별도로 확인합니다. 정적 그래프이므로 녹화 화면이나 실제 기체 영상이라고 표현하지 않습니다.

장애물 비용 최소 기체 여유[m] 접촉 보고 수 최종 목표 오차[m] 결과
약한 비용 (weak_cost) -0.1487 7 1.1838 접촉 충돌을 관측하고 과제 중단 (aborted_contact_collision)
팽창 비용 (inflated_cost) 0.3058 0 0.0820 회피·도착 기준을 만족하고 과제 완료 (avoided_and_arrived)

반지름0.12m 원통과 x500을 감싼 0.40m 구, 추가 여유0.10m를 사용합니다. 비용의 geometry와 실제 월드/scene를 대조하며 최소 거리는 실제 Gazebo pose로 계산합니다. 접촉 보고 수는 서로 다른 충돌 사건의 수가 아닙니다. 약한 비용에서 충돌·여유 침범·정체가 발생하면 그대로 실패로 기록합니다. 안전한 두 후보의 평균이 장애물 안에 들어갈 수 있다는 작은 수치 반례도 실행했습니다. soft 비용은 충돌 회피 보장이 아닙니다.

9-2 자체 모델 폐루프 12조건 중 6조건이 최종 오차0.15m 기준을 통과하지 못했습니다. 통과하려면 12초의 240스텝을 완료하고 최종 3차원 위치 오차가 0.15m 미만이어야 합니다. 온도 변화의 성공만 고르지 않았습니다. 제어기와 자체 모델이 같은 가정을 공유하므로 이 성공을 독립 Gazebo 검증으로 확장하지 않습니다. 의도적으로 잘못된 참조·불완전 평가 구간·변조 소스도 실제로 거부하는지 검사하고 사례 종류를 구별합니다.

약한 장애물 비용에서는 접촉 보고 7개와 이격거리 기준 실패가 관측됐고, 모든 예측 후보가 무효가 되어 명령을 반환하지 못하는 오류(All rollouts invalid; no command returned)로 과제를 중단했습니다. 실제 비행 기록의 과제 완료 여부는 completed_full_task=false입니다. 실패 후 비무장 상태로 복구한 사실이 회피 성공을 뜻하지는 않습니다.

passed=true는 필요한 실행·증거 검증과 묶음 재현이 완료됐다는 뜻입니다. 개별 과제 실패는 cases.success=false로 구별합니다. 의도적으로 잘못된 입력을 거부한 검사는 거부 동작 확인(verification_passed=true)과 입력의 실패(success=false)가 함께 기록될 수 있습니다. 실제 명령·상태·ULog의 대응이 빠지거나 소유 서버가 무장 상태로 남으면 전체 검증 미완료(passed=false)로 끝내고 자료를 보존합니다.

6. 시간·입력·비교의 한계를 읽습니다

| 실제 trial | 외부 계산 평균/최대[ms] | 계산50ms 초과 | 발행 간격 평균/최대[ms] | 발행50ms/60ms 초과 | |---|---|---|---|---|---| | 10_2_hover_move_return | 4.584 / 8.352 | 0 | 52.63 / 56.80 | 563 / 0 | | 11_1_long_horizon | 5.066 / 9.579 | 0 | 52.59 / 59.12 | 562 / 0 | | 11_2_weak_cost | 5.478 / 9.379 | 0 | 52.35 / 58.25 | 232 / 0 | | 11_2_inflated_cost | 5.855 / 14.660 | 0 | 52.63 / 64.81 | 530 / 2 | | 12_1_seed7_px4_position | 0.055 / 0.238 | 0 | 52.63 / 55.39 | 570 / 0 | | 12_1_seed7_mppi | 5.144 / 13.463 | 0 | 52.64 / 64.04 | 563 / 3 |

예측 적분 간격·계산 목표 주기·발행 간격은 서로 다른 값입니다. 실제 OS 스케줄링의 초과를 숨기지 않습니다. 입력 변화량도 c는 무차원, rate는 rad/s, 기준 위치는 m로 성분별 보고합니다. 이를 한 단위처럼 합하거나 물리 에너지·jerk라고 해석하지 않습니다.

12-1의 세 반복·추가 모델 불일치 결과는 원래 로그와 원고로 유지합니다. 이번 한 쌍은 대표 재현이며 새 통계적 우월성의 근거가 아닙니다. 기준에는 외부 속도 feedforward가 없고 MPPI에는 미래 위치·속도 정보가 있습니다. 두 방식 모두 PX4 내부 rate 제어를 사용하며 순수 알고리즘만 떼어낸 비교가 아닙니다.

7. 앞 장의 개념을 파일과 연결합니다

기존 본편 이번 감사·재현에서 연결한 근거
1 실행 환경의 버전·설정·출처 기록을 정리합니다 (provenance/manifest.json).
2 실제 MAVROS·PX4 연결과 원본 ROS bag을 확인합니다.
3 실제 모드·무장 기록과 명령 스트림 손실 처리를 연결합니다. 손실 주입은 10-1 비무장 시험입니다.
4 새 실행 경로에서 colcon으로 offboard_control을 빌드하고 설치된 launch를 실행합니다.
5 온도에 따른 후보 가중치 변화를 수치 예제로 확인합니다 (reference_cost_evaluation).
6 SI 참조 모델과 정규화 모델의 한 단계 병진·자세 계산을 대조합니다. 내부 토크·각속도 동역학의 동일성을 뜻하지 않습니다 (chapter_7).
7-1 원문 소스를 실행해 모터 배분과 단위를 점검합니다 (chapter_7).
7-2 회전행렬·오일러 각 변화율·관성 결합항을 검사합니다 (chapter_7).
8-1 제한 범위로 자른 후보의 가중 평균과 예측 길이를 확인합니다 (reference_cost_evaluation).
8-2 장애물 형상·비용과 안전한 후보의 평균도 충돌할 수 있는 반례를 확인합니다 (obstacle_geometry).
9-1 독립 모델에서 완성된 제어기를 실행하고 예측을 확인합니다. 폐루프·Gazebo·PX4 명령 검증은 이 단계에 포함하지 않습니다 (9_1_independent).
9-2 자체 모델 폐루프 12조건의 성공·실패를 확인합니다 (own_model_closed_loop).
10-1 비무장 상태의 실제 ROS·PX4 명령 전달과 오래된 상태 차단을 확인합니다 (live_ros_px4_interface).
10-2 Gazebo에서 MPPI로 호버링·이동·복귀하는 실제 제어 구간을 확인합니다 (gazebo_mppi_flight).
11-1 Gazebo 시간 궤적 추종을 확인합니다. 이번 재실행은 H20 대표 조건입니다 (gazebo_trajectory_tracking).
11-2 Gazebo 정적 장애물의 접촉·이격거리·도착·중단을 확인합니다. 이번 재실행은 약한 비용과 팽창 비용입니다 (gazebo_static_obstacle_avoidance).
12-1 과거 반복 비교 근거를 보존하고 이번에는 시드7의 PX4 위치 제어·MPPI 한 쌍을 재실행합니다 (gazebo_repeated_comparison).

series_audit.json은 17개 앞선 본편의 보존 원고, 후반부의 실행 소스·원본 로그·첨부 경로와 해시를 연결합니다. 각 대표 과제의 핵심 코드가 당시 실행 소스와 같은지 확인했으며 새 감사·집필 모듈은 따로 기록합니다. 과거 원문을 현재 서버 본문과 같다고 추정하지 않고, 링크 목록의 정합 점검과 현재 HTTP 접속 검증을 구별합니다.

5·8편의 온도 exp(-(S-S_min)/temperature), 제한 범위로 자른 후보의 갱신, 모든 비용에 같은 상수를 더해도 가중치가 바뀌지 않는 성질과 미래 참조의 역할은 작은 실행 예제로 다시 확인했습니다. 8-2의 입력 변화 비용은 물리적 가속도 변화율(jerk)·실측 에너지가 아니며 장애물에는 기체 크기와 여유 거리가 필요합니다. 1~4편의 환경·연결·Offboard·패키지 설명은 실제 버전, 모드·명령 스트림 손실 기록과 새 경로의 빌드·launch 실행으로 연결했습니다. 오래된 설치 명령 전부를 새 OS에서 검증했다고 주장하지 않습니다.

목차 /83과 비교 평가 /130의 구체적인 세 줄 요약·마지막 연결 제안은 companion_revision.md에 있습니다. 기존 원문과 정확한 ID, 변경 이유와 근거를 함께 남겼으며 서버에 적용하지 않았습니다. /97·/98의 비공개 보호 범위도 유지합니다.

8. 재현 자료와 연재의 마무리

 

series_12_2_code.zip
0.42MB

압축 파일에는 이번 실행의 Python 소스, offboard_control 패키지, server config, 재현 README와 앞 장의 근거 지도·로컬 보완안이 들어 있습니다. 실행 결과 폴더에는 아래 구조로 원본을 보존합니다.

result.json                  # 전체 판정, 입력/좌표계/한계, 소스 해시
source/                      # 실행 소스와 설정의 사본
series_audit.json             # 과거 본편의 버전·원고·로그·첨부 지도
checks/                      # SI/회전/비용/참조/평가/변조 거부 검사
models/                      # 독립 계산과 12조건 자체 모델 원본
trials/                      # 비무장 연결과 여섯 실제 비행의 원본
  */rosbag/*.mcap             # 실제 ROS 상태와 발행 명령
  */runtime/log/**/*.ulg     # FCU mode, 수신 입력, 응답
  */provenance/              # 실제 바이너리/자산/버전/diff
archive_reproduction.json     # 새 추출 경로 명령·판정
archive_reproduction.log      # 하위 재현 표준 출력과 오류
companion_revision.md         # 기존 글 로컬 보완안

실행한 코드와 단위·시각·실제 제어 계층을 연결해 읽는 것이 이 연재의 도착점입니다. 12-2에서 확정 목차를 마무리하며 임의 후속 회차를 만들지 않습니다. 실제 기체, 인지·SLAM·동적 장애물, 바람과 강인성 보장은 이번 결과 밖이며 별도의 조건과 실험이 필요합니다.

코드 근거: ZIP의 controller.py, ros_interface.py, rigid_body_reference.py, trajectory.py, obstacles.py와 각 *_results.py의 검증 구현을 참고해 주세요. 공식 구현은 MAVROS 2.14.0의 상태 좌표 변환 (local_position.cpp), 추력·몸체 각속도 목표 변환 (setpoint_raw.cpp), 기록한 PX4 commit의 명령 수신 구현 (mavlink_receiver.cpp)에서 확인하실 수 있습니다. 실행 시각·정확한 파일 해시·원본 참조는 series_audit.json과 각 시도의 실행 환경 기록(provenance/)에 있습니다.

반응형