Onboard and post-flight software for the GARUDA CanSat terrain and ground mapping payload. The payload runs on a Raspberry Pi 5, collects synchronized GPS, barometer, AHRS/IMU, camera, gimbal, and telemetry data during descent, logs the mission to CSV, and processes recovered mission data after flight.
The project currently defaults to simulation mode, so it can be developed and tested without connected flight hardware.
The simulation path is the primary working flow. It can generate a fake descent, capture mock images, log telemetry, and export map products without hardware. Real hardware adapters are wired for the tested Garud HAT reference configuration. Run the hardware checks on the Raspberry Pi before mission use, because desktop development still defaults to mock hardware.
Post-flight terrain reconstruction has a tested V2 path for external aerial datasets such as the Wietrznia OpenDroneMap DJI image set. The V2 path now executes GARUDA quality filtering, graph-based matching, multi-view track diagnostics, PyCOLMAP sparse SfM, and global bundle adjustment. Dense COLMAP PatchMatch is wired as a post-flight-only backend, but it requires CUDA; on the current Windows test machine dense MVS failed at that CUDA requirement, so DSM and true terrain orthorectification were skipped honestly.
- Simulated GPS track near Pune, India.
- Simulated descent from roughly 700 m altitude.
- Mock camera image capture with GPS text overlays.
- Mock IMU/AHRS, barometer, gimbal, and telemetry workers.
- Threaded mission runtime with shared payload state.
- CSV mission logging with image timestamps, angular velocity, raw IMU, and AHRS metadata.
- AHRS-assisted pose priors for post-flight image normalization.
- Post-flight image quality scoring for blur, exposure, tilt, and motion.
- Graph-based image relationship candidates for non-sequential matching.
- Dataset-mode post-flight runner for DJI aerial image folders.
- PyCOLMAP sparse SfM import from GARUDA verified features and matches.
- Bundle-adjusted camera pose export and sparse reconstruction diagnostics.
- Optional COLMAP dense MVS adapter isolated from flight runtime.
- DSM rasterization from dense PLY point clouds when dense MVS succeeds.
- Interactive Folium HTML map export.
- Google Earth compatible KML export.
- Estimated camera ground footprints and unique coverage area.
- Garud HAT hardware adapters for BNO085 on I2C1, BMP388 on SPI0, GPS-over-SC16IS750, XBee, and PCA9685 gimbal control.
- Standalone hardware bring-up scripts for Raspberry Pi testing.
- Python 3.9+
- Raspberry Pi target platform
- Folium for interactive HTML maps
- SimpleKML for Google Earth exports
- Pillow/OpenCV for image handling
- Adafruit CircuitPython libraries for supported hardware modules
The mapping stack is split into flight-safe capture/logging code and expensive
post-flight reconstruction code. Flight runtime still records images and
metadata only. Heavy work stays under processing/, mapping/, vision/,
sensor_fusion/, and storage/.
The current V2 post-flight pipeline can run on a folder of DJI images:
image dataset
-> quality scoring
-> temporal/GPS candidate graph
-> SIFT feature extraction and cache
-> FLANN/BF matching
-> Essential/Fundamental/Homography verification
-> multi-view track diagnostics
-> PyCOLMAP database import
-> incremental sparse SfM
-> global bundle adjustment
-> camera pose and sparse model export
-> optional dense PatchMatch and DSM if CUDA is available
The preview JPEGs are diagnostic products. A true orthomosaic is only claimed when dense MVS, DSM generation, and terrain-based orthorectification complete. On the latest Wietrznia test, sparse SfM succeeded and dense MVS was blocked by missing CUDA.
ground_mapping_payload/
|-- camera/ Camera factory and mock/real camera classes
|-- core/ Shared state, mission states, thread manager, health
|-- data/ Runtime output folders for logs, images, and maps
|-- docs/ Wiring, pin map, checklist, and user manual
|-- gimbal/ Gimbal stabilizer and servo control
|-- hardware_tests/ Real hardware bring-up scripts
|-- logging_system/ CSV logger
|-- mapping/ Fake flight data, HTML map, KML, geotag helpers
|-- processing/ Offline mission validation and preprocessing
|-- sensor_fusion/ AHRS estimators, quaternion helpers, and pose priors
|-- storage/ Mission manifest and metadata records
|-- sensors/ GPS, IMU, and barometer interfaces
|-- telemetry/ LoRa/XBee telemetry packet generation/sending
|-- vision/ Undistortion, pose normalization, features, matching
|-- tests/ Simulation and module tests
|-- config.py Runtime configuration
|-- main.py Main mission entry point
|-- requirements.txt Python dependencies
`-- README.md
- Python 3.9 or newer
- Raspberry Pi 5 target for hardware mode
- Garud HAT with BNO085 IMU on I2C1, BMP388 barometer on SPI0 CS GPIO8, NEO-M8N GPS through SC16IS750, PCA9685 gimbal, XBee telemetry, and camera
Python packages are listed in requirements.txt.
Clone the repository:
git clone https://github.com/TARSR/GARUD.git
cd GARUDCreate a virtual environment:
python -m venv venvWindows:
venv\Scripts\activate
pip install -r requirements.txtLinux / Raspberry Pi:
source venv/bin/activate
pip install -r requirements.txtRun the full simulated mission:
python tests/test_full_simulation.pyRun the main payload program:
python main.pyPress Ctrl+C to stop the main program cleanly. When logging and mapping are
enabled, the program generates output maps during shutdown.
Generate a fake flight log and maps:
python tests/test_fake_mapping.pyRun the main simulation/module checks:
python tests/test_gps.py
python tests/test_imu.py
python tests/test_barometer.py
python tests/test_camera.py
python tests/test_telemetry.py
python tests/test_ahrs.pyRun all pytest-style tests if pytest is installed:
python -m pytest testsRun hardware checks on Raspberry Pi:
python hardware_tests/test_i2c_scan.py
python hardware_tests/test_camera_real.py
python hardware_tests/test_gps_real.py
python hardware_tests/test_barometer_real.py
python hardware_tests/test_imu_real.py
python hardware_tests/test_ahrs_real.py --mode bno085
python hardware_tests/test_servo_real.py
python hardware_tests/test_gimbal_real.py
python hardware_tests/test_xbee_real.py
python hardware_tests/test_all_sensors_status.py| Path | Description |
|---|---|
data/images/ |
Captured mock or real images |
data/logs/ |
Mission CSV logs |
data/maps/flight_path.html |
Interactive flight-path map |
data/maps/flight_path.kml |
Google Earth KML export |
data/logs/hardware_tests/ |
Hardware test logs |
mapping_output/terrain_mapping_test_v2/ |
Checked-in Wietrznia V2 result images and diagnostics |
Runtime output folders are kept in the repository with .gitkeep files, while
generated logs, images, and maps are ignored by Git.
Install normal flight/runtime dependencies first:
pip install -r requirements.txtInstall heavy post-flight dependencies only on the development/reconstruction machine:
pip install -r requirements-postflight.txtRun a small Wietrznia-style dataset test:
python -m processing.run_dataset_test ^
--images "D:\RESOURCES\Terrain dataset\images" ^
--output "output\terrain_mapping_test_v2\small_25" ^
--profile fast ^
--max-images 25 ^
--neighbors 4 ^
--feature-max-dim 1024 ^
--enable-dense ^
--dense-max-image-size 900Latest checked-in V2 test summary:
Images selected: 25
Good images: 25
Candidate edges: 53
Verified edges: 53
Sparse SfM: SUCCESS
Registered images: 25 / 25
Sparse points: 9,329
Mean reprojection error after BA: 1.163 px
Dense MVS: FAILED - CUDA unavailable
DSM / true orthomosaic: SKIPPED
Overall: PARTIAL
Important outputs:
| Path | Description |
|---|---|
mapping_output/terrain_mapping_test_v2/final/global_pose_preview.jpg |
Sparse reconstruction and camera trajectory preview |
mapping_output/terrain_mapping_test_v2/final/before_after_comparison.jpg |
Baseline vs V2 diagnostic comparison |
mapping_output/terrain_mapping_test_v2/diagnostics/reconstruction_report.json |
Full run report |
mapping_output/terrain_mapping_test_v2/diagnostics/dense_metrics.json |
Dense MVS status and CUDA blocker |
mapping_output/terrain_mapping_test_v2/diagnostics/camera_poses.csv |
Bundle-adjusted camera pose export |
timestamp,mission_time,state,latitude,longitude,gps_altitude,baro_altitude,roll,pitch,yaw,gyro_x,gyro_y,gyro_z,image_name,image_timestamp,battery,status,ahrs_enabled,ahrs_source,ahrs_valid,ahrs_healthy,ahrs_confidence,quat_w,quat_x,quat_y,quat_z,ahrs_roll,ahrs_pitch,ahrs_yaw,attitude_accuracy_rad,imu_sample_age_ms,accel_correction_active,mag_correction_active,ahrs_timestamp_ns,raw_accel_x,raw_accel_y,raw_accel_z,raw_mag_x,raw_mag_y,raw_mag_z,raw_quat_w,raw_quat_x,raw_quat_y,raw_quat_z
Edit config.py to enable/disable modules, switch between mock and real
hardware, adjust capture/logging intervals, and set Garud HAT bus/pin values.
Important settings:
USE_MOCK_HARDWARE = True
ENABLE_CAMERA = True
ENABLE_GPS = True
ENABLE_MAPPING = True
GPS_TRANSPORT = "SC16IS750_SPI"
XBEE_SERIAL_PORT = "/dev/ttyAMA0"
BNO085_TRANSPORT = "I2C"
BNO085_I2C_ADDRESS = 0x4A
BMP388_CS_PIN = 8
GPS_SC16IS750_CS_PIN = 7
PCA9685_I2C_ADDRESS = 0x40
ULN2003_IN1_PIN = 25
ULN2003_IN2_PIN = 24
ULN2003_IN3_PIN = 23
ULN2003_IN4_PIN = 18
ENABLE_AHRS = True
AHRS_MODE = "BNO085"
AHRS_RATE_HZ = 100Set USE_MOCK_HARDWARE = False on the Raspberry Pi after installing
requirements.txt and confirming the HAT wiring.
Mapping footprint settings:
CAMERA_HORIZONTAL_FOV_DEG = 62.2
CAMERA_VERTICAL_FOV_DEG = 48.8
MAPPING_COVERAGE_GRID_M = 5.0docs/USER_MANUAL.md- operator manual and workflowdocs/architecture_pose_normalization.md- V1 pose-assisted mapping designdocs/postflight_terrain_mapping.md- V2 dataset reconstruction workflow and current limitationsdocs/flight_flow.md- mission state sequencedocs/wiring_plan.md- wiring notesdocs/pin_map.md- Raspberry Pi pin assignmentsdocs/test_checklist.md- bring-up and field checklistdocs/component_status.md- subsystem readiness status
| File | Class / area |
|---|---|
bus_manager.py |
Shared I2C1/SPI0 bus initialization |
sensors/gps.py |
RealGPS using NEO-M8N through SC16IS750 on SPI0 CE1 |
sensors/imu.py |
RealIMU using BNO085 on I2C1 address 0x4A |
sensors/barometer.py |
RealBarometer using BMP388 on SPI0 with CS_BMP GPIO8 |
camera/mock_camera.py |
Real Raspberry Pi camera capture |
telemetry/xbee_sender.py |
RealTelemetry using XBee on /dev/ttyAMA0 |
gimbal/servo_control.py |
RealGimbal using PCA9685 servo control |
The mapping, logging, telemetry, and post-flight processing pipelines are kept independent of the hardware adapters. Mock mode remains the default for local development and CI-style checks.
Internal project for the GARUDA CanSat / TARSR team.