Developer Guide¶
This section provides guidance for developers working with ArduPilot.
Getting Started¶
Building ArduPilot¶
Prerequisites:
GCC/Clang compiler for ARM
CMake or Waf build system
MAVLink headers
Basic Build:
# Configure for a specific vehicle
./waf configure --board CUAV_NANO
./waf copter
# Or for Plane
./waf plane
# Or for Rover
./waf rover
Common Build Targets:
copter - Multicopter firmware
plane - Fixed-wing firmware
rover - Rover firmware
sub - Submarine firmware
AntennaTracker - Antenna tracker
Tools - Tools and utilities
Code Structure¶
Directory Layout¶
ardupilot/
├── libraries/
│ ├── AP_HAL/ # Hardware Abstraction Layer
│ ├── AC_AttitudeControl/ # Attitude control
│ ├── AC_PID/ # PID controllers
│ ├── AC_WPNav/ # Waypoint navigation
│ ├── AP_AHRS/ # AHRS
│ ├── AP_GPS/ # GPS driver
│ ├── AP_Baro/ # Barometer
│ ├── AP_Compass/ # Compass
│ ├── AP_BattMonitor/ # Battery monitor
│ └── ...
├── ArduCopter/ # Copter firmware
├── ArduPlane/ # Plane firmware
└── ArduRover/ # Rover firmware
Key Concepts¶
Singleton Pattern¶
Most ArduPilot classes use the singleton pattern:
// Get singleton
AP_GPS *gps = AP_GPS::get_singleton();
// Check availability
if (gps == nullptr) {
// Not available on this platform
}
// Use
gps->update();
Location loc = gps->location();
Update Loop¶
Sensors and controllers must be updated in the main loop:
void loop() {
// 100Hz tasks
ahrs.update();
motors->output();
// 50Hz tasks
gps->update();
baro->update();
// 10Hz tasks
gcs()->send_message(HEARTBEAT);
// Wait for next loop
scheduler->delay(1);
}
HAL (Hardware Abstraction Layer)¶
Access hardware through HAL:
// UART/Serial
AP_HAL::UARTDriver *uart = hal.uartD[0]; // Serial0
uart->printf("Hello\n");
// GPIO
hal.gpio->pinMode(13, HAL_GPIO_OUTPUT);
hal.gpio->digitalWrite(13, HAL_GPIO_HIGH);
// ADC
uint16_t adc_value = hal.adc->ch(0);
// Timer
hal.scheduler->register_timer_process(callback);
Parameter System¶
Define parameters in your class:
// In header file
class MyClass {
public:
static const struct AP_Param::GroupInfo var_info[];
private:
AP_Float _my_param {"my_param", 0.5f};
AP_Int8 _my_mode {"my_mode", 0};
};
// In source file
const struct AP_Param::GroupInfo MyClass::var_info[] = {
{"my_param", 0, var_info::PARAM_FLOAT, _my_param, 0, 0, 0, 1.0},
{"my_mode", 0, var_info::PARAM_INT8, _my_mode, 0, 0, 0, 1},
var_info::END
};
Logging¶
Log data using DataFlash:
#include <DataFlash.h>
// Define log structure
struct PACKED log_MyStruct {
LOG_PACKET_HEADER;
uint64_t timestamp;
float data;
};
// Write log
DataFlash::instance()->Log_Write_MyStruct(timestamp, data);
// Log format (in GetMessageInfo)
{ "MYS", "QF", "TimeUS,Data", "TimeUS,Data" }
Debugging¶
Serial Debug¶
// Simple debug
Serial.printf("Value: %f\n", value);
// Conditional debug
#if DEBUG_LEVEL > 0
debug("Function: value=%f", value);
#endif
MAVLink Debug¶
// Send debug message to GCS
gcs().send_text(MAV_SEVERITY_DEBUG, "Debug: %f", value);
Common Patterns¶
Flight Mode Implementation¶
class ModeMyMode : public Mode {
public:
ModeMyMode() : Mode("MYMODE") {}
void init() override {
// Called when mode starts
}
void run() override {
// Called every loop in this mode
// Control motors here
}
bool requires_arming() override { return true; }
};
Sensor Driver¶
class MySensor_Backend : public AP_Sensor_Backend {
public:
MySensor_Backend(AP_Sensor &frontend, uint8_t instance);
void update() override {
// Read sensor
// Publish data
frontend.set_status(OK);
frontend.set_data(distance);
}
};
Testing¶
SITL (Software In The Loop)¶
# Run Copter SITL
cd ArduCopter
../Tools/autotest/sim_vehicle.py --map --console
# Run Plane SITL
cd ArduPlane
../Tools/autotest/sim_vehicle.py --map --console
SITL Commands¶
# Arm/Disarm
mode guided
arm throttle
disarm
# Takeoff
takeoff 10
# Fly to location
wp set 123456789 987654321 20
# Land
mode land