一个完整的无人机项目通常由两个核心部分组成:飞行控制器和机载计算机。

- 飞行控制器:这是无人机的“大脑和神经系统”,它负责实时处理传感器数据(陀螺仪、加速度计等),并通过 PWM 信号控制电机的转速,以维持飞行姿态或执行飞手指令。Arduino 非常适合担任这个角色,特别是对于入门级和 DIY 无人机。
- 机载计算机:这是无人机的“思考中枢”,它负责运行更复杂的算法,如自主飞行路径规划、计算机视觉(目标跟踪、避障)、与地面站的通信等,这个角色通常由性能更强的 Raspberry Pi (树莓派) 或 NVIDIA Jetson 等设备担任。
Arduino 无人机源代码主要指的是飞行控制器的固件,下面我将为您分解这个固件的构成,并提供一个基于 Arduino 的、简化的多旋翼无人机源代码示例。
Arduino 无人机源代码的核心组成部分
一个成熟的 Arduino 无人机固件,无论多么复杂,都离不开以下几个模块:
-
传感器数据融合:
- IMU (惯性测量单元):这是最关键的传感器,包含陀螺仪(测量角速度)和加速度计(测量加速度/重力方向)。
- MPU6050 或 MPU9250 是最常用的 IMU 传感器。
- 传感器数据通过 I2C 通信协议读取。
- 由于陀螺仪有漂移,加速度计有噪声,需要使用卡尔曼滤波或互补滤波算法将两者数据融合,得到准确、稳定的姿态(俯仰角、横滚角、偏航角)。
-
控制算法:
(图片来源网络,侵删)- PID 控制器:这是无人机稳定飞行的灵魂。
- P (Proportional - 比例):根据当前姿态与期望姿态的误差大小,成比例地进行修正,误差越大,修正力越大。
- I (Integral - 积分):消除稳态误差,由于风的影响,飞机会持续向一边倾斜,P 控制无法完全修正,I 控制会累积这个持续的误差,并产生一个持续的修正力。
- D (Derivative - 微分):抑制震荡,提高响应速度,它根据误差变化率来预测未来的误差,并进行提前修正,防止过冲。
- 需要为俯仰、横滚、偏航三个轴分别设置 PID 参数。
-
电机控制:
- 使用 Arduino 的
analogWrite()函数(或更高效的定时器库)生成 PWM (脉冲宽度调制) 信号。 - PWM 信号控制连接到电子调速器 的引脚,ESC 再控制无刷电机的转速。
- 四旋翼无人机的油门输出通常是四个电机 PWM 值的合成,以实现升降、前后、左右、旋转等动作。
- 使用 Arduino 的
-
遥控信号接收:
- 飞手通过遥控器发送指令(油门、俯仰、横滚、偏航)。
- Arduino 通过读取PPM 信号或多个PWM 信号来获取这些指令,对于 PPM 信号,通常使用一个中断引脚来解码。
-
通信与调试:
- 通过 Serial 串口输出传感器数据、PID 输出、电机值等,方便在 Arduino IDE 的串口监视器中进行调试。
简化的 Arduino 四旋翼无人机源代码示例
以下是一个功能非常基础的示例,旨在演示核心控制逻辑。它不能直接用于飞行,因为它缺少了安全特性(如失控保护、失控自动降落等),PID 参数需要大量调试才能稳定。

这个例子假设您已经:
- 连接好了 MPU6050 传感器。
- 连接好了 4 个 ESC 和电机。
- 连接好了 PPM 信号接收器。
/*
* Arduino Simple Quadcopter Flight Controller
* 这是一个极度简化的示例,仅用于理解基本原理。
* 请勿直接用于实际飞行!
*/
#include <Wire.h>
#include <I2Cdev.h>
#include <MPU6050.h>
#include <PID_v1.h> // 需要安装 PID 库
// --- 硬件引脚定义 ---
#define MOTOR_PIN_1 3 // 电机1的PWM引脚 (后右)
#define MOTOR_PIN_2 5 // 电机2的PWM引脚 (后左)
#define MOTOR_PIN_3 6 // 电机3的PWM引脚 (前左)
#define MOTOR_PIN_4 9 // 电机4的PWM引脚 (前右)
#define PPM_PIN 2 // PPM信号接收引脚 (需要中断)
// --- MPU6050 对象 ---
MPU6050 mpu;
// --- 全局变量 ---
float roll_angle, pitch_angle, yaw_angle; // 实际姿态角度
float roll_input, pitch_input, yaw_input; // PID输入 (当前角度)
float roll_output, pitch_output, yaw_output; // PID输出
float roll_setpoint = 0, pitch_setpoint = 0, yaw_setpoint = 0; // PID设定值 (期望角度)
// --- PID 控制器定义 ---
// 参数需要根据你的无人机硬件和重量进行大量调试!
double Kp_roll = 1.0, Ki_roll = 0.01, Kd_roll = 0.1;
double Kp_pitch = 1.0, Ki_pitch = 0.01, Kd_pitch = 0.1;
double Kp_yaw = 1.0, Ki_yaw = 0.01, Kd_yaw = 0.1;
PID rollPID(&roll_input, &roll_output, &roll_setpoint, Kp_roll, Ki_roll, Kd_roll, DIRECT);
PID pitchPID(&pitch_input, &pitch_output, &pitch_setpoint, Kp_pitch, Ki_pitch, Kd_pitch, DIRECT);
PID yawPID(&yaw_input, &yaw_output, &yaw_setpoint, Kp_yaw, Ki_yaw, Kd_yaw, DIRECT);
// --- 遥控器变量 ---
int ppm_channels[8]; // 存储PPM信号的8个通道值
unsigned long ppm_last_time;
// --- 初始化 ---
void setup() {
Serial.begin(57600);
Wire.begin();
// 初始化MPU6050
mpu.initialize();
if (!mpu.testConnection()) {
Serial.println("MPU6050 connection failed!");
while (1);
}
// 初始化PID控制器
rollPID.SetMode(AUTOMATIC);
pitchPID.SetMode(AUTOMATIC);
yawPID.SetMode(AUTOMATIC);
rollPID.SetOutputLimits(-100, 100); // 限制PID输出范围
pitchPID.SetOutputLimits(-100, 100);
yawPID.SetOutputLimits(-100, 100);
// 初始化电机引脚
pinMode(MOTOR_PIN_1, OUTPUT);
pinMode(MOTOR_PIN_2, OUTPUT);
pinMode(MOTOR_PIN_3, OUTPUT);
pinMode(MOTOR_PIN_4, OUTPUT);
// 初始化PPM输入中断
pinMode(PPM_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PPM_PIN), ppm_interrupt, RISING);
Serial.println("Quadcopter Initialized. Waiting for signal...");
}
// --- 主循环 ---
void loop() {
// 1. 读取遥控器信号
read_ppm_signal();
// 2. 读取并融合IMU数据
get_mpu_data();
// 3. 更新PID设定值(来自遥控器)
// 假设通道1是横滚,通道2是俯仰,通道4是偏航
roll_setpoint = map(ppm_channels[0], 1000, 2000, -30, 30); // -30到30度
pitch_setpoint = map(ppm_channels[1], 1000, 2000, -30, 30);
yaw_setpoint = map(ppm_channels[3], 1000, 2000, -100, 100); // 偏航用速度控制
// 4. 计算PID输出
roll_input = roll_angle;
pitch_input = pitch_angle;
yaw_input = yaw_angle;
rollPID.Compute();
pitchPID.Compute();
yawPID.Compute();
// 5. 计算并设置电机PWM值
// 这是一个非常简化的混合逻辑,实际混合更复杂
int base_throttle = ppm_channels[2]; // 油门通道
int motor1_val = base_throttle + pitch_output - roll_output - yaw_output;
int motor2_val = base_throttle - pitch_output - roll_output + yaw_output;
int motor3_val = base_throttle - pitch_output + roll_output + yaw_output;
int motor4_val = base_throttle + pitch_output + roll_output - yaw_output;
// 限制PWM值在有效范围内 (1000-2000)
motor1_val = constrain(motor1_val, 1000, 2000);
motor2_val = constrain(motor2_val, 1000, 2000);
motor3_val = constrain(motor3_val, 1000, 2000);
motor4_val = constrain(motor4_val, 1000, 2000);
set_motor_speeds(motor1_val, motor2_val, motor3_val, motor4_val);
// 5. 调试信息输出
debug_output();
delay(10); // 控制循环频率,大约100Hz
}
// --- 函数定义 ---
// 简化的MPU数据读取和角度计算(实际应使用滤波)
void get_mpu_data() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// 简单地通过加速度计计算角度(非常不精确,仅用于演示)
roll_angle = atan2(ay, az) * 180 / PI;
pitch_angle = atan2(-ax, az) * 180 / PI;
// 注意:偏航角不能通过加速度计获得,需要陀螺仪积分或磁力计
yaw_angle = 0;
}
// PPM信号中断处理函数
void ppm_interrupt() {
static unsigned long ppm_start_time;
static int ppm_channel;
if (micros() - ppm_start_time > 5000) { // PPM帧头
ppm_channel = 0;
}
if (ppm_channel < 8) {
ppm_channels[ppm_channel] = micros() - ppm_start_time;
ppm_channel++;
}
ppm_start_time = micros();
}
// 设置电机速度
void set_motor_speeds(int m1, int m2, int m3, int m4) {
// 注意:大多数ESC需要1000-2000us的脉冲
// Arduino的analogWrite()是0-255,需要转换或使用Servo库
// 这里简化处理,假设硬件层已处理好
analogWrite(MOTOR_PIN_1, m1 / 8); // 粗略转换
analogWrite(MOTOR_PIN_2, m2 / 8);
analogWrite(MOTOR_PIN_3, m3 / 8);
analogWrite(MOTOR_PIN_4, m4 / 8);
}
// 调试信息输出
void debug_output() {
Serial.print("Roll: "); Serial.print(roll_angle);
Serial.print(" Pitch: "); Serial.print(pitch_angle);
Serial.print(" Yaw: "); Serial.print(yaw_angle);
Serial.print(" | R_out: "); Serial.print(roll_output);
Serial.print(" P_out: "); Serial.print(pitch_output);
Serial.print(" Y_out: "); Serial.println(yaw_output);
}
获取更成熟、更完整的源代码
上面的示例仅用于教学,对于实际项目,强烈建议使用现有的成熟开源飞控固件,它们经过了大量测试,非常稳定和安全。
Cleanflight / Betaflight (基于 STM32, 但是行业标杆)
虽然 Cleanflight 和 Betaflight 主要为 STM32 芯片设计,但它们是现代多旋翼飞控的“圣经”,你可以从它们的源码中学到:
- 极其成熟的 PID 算法和调试方法。
- DShot 协议等先进的电机通信协议。
- 陀螺仪和加速度计的精确滤波(如 Madgwick, Mahony)。
- 失控保护、低电压保护等安全机制。
- 配置系统(通过串口或GUI)。
学习建议:即使你不用 STM32,也应该去阅读它们的源码,特别是 src/main/flight/pid.c 和 src/main/sensors/ 目录下的代码,理解其实现思想。
ArduPilot (支持 Arduino 和更高级平台)
ArduPilot 是一个功能极其强大的开源飞控项目,支持固定翼、多旋翼、直升机等多种机型,它有专门的 ArduCopter(多旋翼)固件。
- 特点:
- 功能全面,支持自主飞行、RTL(返航)、定高、定点等高级功能。
- 有非常活跃的社区和详尽的文档。
- 支持 Arduino (如 Mega 2560), 但也支持 Pixhawk 等性能更强的硬件。
- 获取源码:
- GitHub: https://github.com/ArduPilot/ardupilot
- 官方文档: https://ardupilot.org/
如果你想在 Arduino 上实现一个功能完整的无人机,直接基于 ArduPilot 进行二次开发是最好的选择。
MultiWii (经典入门级 Arduino 飞控)
MultiWii 是最早将 Arduino 用于 DIY 无人机的项目之一,非常经典,现在已经不太活跃,但其代码结构清晰,非常适合初学者学习。
- 特点:
- 代码结构简单,易于理解。
- 完全基于 Arduino (ATmega/AVR)。
- 是学习飞控原理的绝佳教材。
- 获取源码:
总结与建议
- 从学习开始:如果你是初学者,不要急于写代码,先理解传感器融合和PID控制的原理。
- 从示例入手:使用上面提供的简化代码,搭建一个最小的测试平台(比如用电机测试台),在确保安全的情况下,尝试调试 PID 参数,观察电机响应。
- 借鉴成熟项目:直接从零写一个稳定可靠的飞控固件难度极高,强烈建议研究 ArduPilot 或 MultiWii 的源码,理解其架构和实现,然后在此基础上进行修改和扩展。
- 安全第一:无人机是高速旋转的设备,非常危险,在调试时,务必使用螺旋桨保护罩,并确保在空旷、安全的环境下进行测试。
希望这份详细的指南能帮助你开启 Arduino 无人机的开发之旅!
