第一部分:核心概念与工作原理
在开始之前,你需要理解无人机是如何飞行的。

-
基本结构:
- 机架: 无人机的骨架,连接所有部件。
- 电机与螺旋桨: 4 个电机,每个电机带动一个螺旋桨,提供升力。
- 电调: 电子调速器,接收来自控制板的 PWM 信号,控制电机的转速。
- 飞行控制板: 核心大脑,负责处理传感器数据、计算控制指令,并向电调发送信号。
- 电池: 提供动力,通常是 LiPo 电池。
- 遥控器与接收机: 用于手动控制无人机起飞、转向、飞行模式切换等。
-
飞行姿态与控制: 无人机通过改变四个电机的转速来实现不同姿态的飞行。
- 俯仰: 无人机前后倾斜,通过增加后侧两个电机的转速,同时减小前侧两个电机的转速实现前进。
- 横滚: 无人机左右倾斜,通过增加右侧两个电机的转速,同时减小左侧两个电机的转速实现向右飞行。
- 偏航: 无人机水平旋转,通过对角线上两个电机的转速增加,另外两个对角线上的电机转速减小(左前+右后,右前+左后)实现,这是由于螺旋桨的反扭矩效应。
- 升降: 所有四个电机的同时增加或减小转速,实现上升或下降。
-
传感器与姿态解算: 控制板需要知道无人机当前的姿态(角度)才能进行稳定控制,这通常通过以下传感器组合实现:
- 陀螺仪: 测量角速度(旋转速度),但它有温漂问题,数据会随时间产生偏差。
- 加速度计: 测量加速度(包括重力加速度),可以用来测量静态姿态(俯仰角和横滚角),但在运动中(比如飞行时)数据会非常“抖动”且不准确。
- 磁力计: 测量地磁场,用于确定机头朝向(航向角)。
传感器融合: 单独使用任何一个传感器都不行,我们需要将陀螺仪、加速度计和磁力计的数据融合起来,得到一个准确、平滑的姿态估计,最常用的算法是 Madgwick 或 Mahony 算法,幸运的是,很多现成的库已经帮我们实现了这些算法。
(图片来源网络,侵删)
第二部分:所需硬件清单
对于初学者,我强烈建议使用集成了传感器和必要电路的开发板,而不是从零开始焊接所有元件。
初学者首选(强烈推荐)
- 飞行控制板: Arduino Mega 2560 + GY-68 (MPU-6050) 传感器板,这是最经典、资料最多的组合,MPU-6050 集成了 3 轴陀螺仪和 3 轴加速度计,你需要自己添加一个磁力计(如 HMC5883L)。
- 升级版: Arduino Due + GY-85 (或类似的 10DOF 传感器板),Due 性能更强,可以处理更复杂的传感器融合,GY-85 通常集成了 MPU-6050、HMC5883L 和气压计(用于高度测量)。
集成度更高(推荐有一定基础后使用)
- 飞行控制板: MultiWii, Cleanflight, Betaflight 等开源固件的开发板(如 Naze32, Flip32, SPRacingF3),这些板子性能强大,但配置和调试相对复杂,通常需要使用专门的配置软件(如 Cleanflight Configurator),它们的核心也是基于 Arduino 或类似架构的,但底层优化得更好。
通用硬件清单
- 主控板: Arduino Mega 2560 或 Arduino Due。
- IMU 传感器模块: GY-68 (MPU-6050) + HMC5883L (磁力计)。
- 电机: 4 个无刷直流电机(KV 值根据你的机架大小选择,如 1006 KV1500)。
- 电调: 4 个 2-4S 的无刷电调,确保支持你选择的电机和电池。
- 螺旋桨: 2 对(4 个)正反桨(2 个 CW 顺时针,2 个 CCW 逆时针)。
- 机架: 150mm 或 200mm 的四轴机架。
- 电池: 1S 或 2S 的 LiPo 电池(1S 更安全,动力稍弱;2S 动力强,但风险也高)。
- 遥控器与接收机: 至少 4 通道的遥控器(用于油门、副翼、升降、方向)和配套的接收机。
- 其他: 杜邦线、香蕉线(连接电池和电调)、热缩管、扎带、螺丝等。
第三部分:软件架构与代码
这是项目的核心,我们将分步实现。
环境准备
- 安装 Arduino IDE。
- 安装必要的库:
Wire.h: 用于 I2C 通信,与 MPU-6050 通信必需。I2Cdev.h: MPU-6050 的底层驱动库。MPU6050.h: MPU-6050 的上层库。HMC5883L.h: 磁力计库。Madgwick.h或Mahony.h: 传感器融合库(推荐 Madgwick)。PID_v1.h: PID 控制算法库。
硬件连接
- IMU -> Arduino:
VCC->5VGND->GNDSCL->SCL(Arduino Mega: 21, Arduino Due: 22)SDA->SDA(Arduino Mega: 20, Arduino Due: 21)
- 电调 -> Arduino:
- 每个电调的信号线连接到 Arduino 的一个 PWM 引脚(
3, 5, 6, 9)。注意: 这些引脚必须支持analogWrite()。
- 每个电调的信号线连接到 Arduino 的一个 PWM 引脚(
- 接收机 -> Arduino:
- 接收机的通道信号线连接到另外的 PWM 引脚(
2, 4, 7, 8)。
- 接收机的通道信号线连接到另外的 PWM 引脚(
代码实现步骤
我们将代码分为几个模块:
步骤 1:传感器初始化与读取

#include <Wire.h>
#include <I2Cdev.h>
#include <MPU6050.h>
#include <HMC5883L.h>
#include <Madgwick.h>
// 创建传感器对象
MPU6050 mpu;
HMC5883L mag;
// 创建 Madgwick 滤波器对象
Madgwick filter;
// 陀螺仪和加速度计原始数据
int16_t ax, ay, az, gx, gy, gz;
// 磁力计原始数据
int16_t mx, my, mz;
void setup() {
Serial.begin(57600);
Wire.begin();
// 初始化 MPU6050
mpu.initialize();
if (!mpu.testConnection()) {
Serial.println("MPU6050 connection failed!");
while(1);
}
// 初始化 HMC5883L
mag.initialize();
if (!mag.testConnection()) {
Serial.println("HMC5883L connection failed!");
while(1);
}
// 设置 Madgwick 滤波器采样频率
filter.begin(100); // 假设 loop 函数每秒运行100次
}
void loop() {
// 读取所有传感器数据
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
mag.getHeading(&mx, &my, &mz);
// 将原始数据转换为物理单位
float accX = ax / 16384.0; // 假设加速度计灵敏度为 ±2g
float accY = ay / 16384.0;
float accZ = az / 16384.0;
float gyroX = gx / 131.0; // 假设陀螺仪灵敏度为 ±250 deg/s
float gyroY = gy / 131.0;
float gyroZ = gz / 131.0;
// 更新 Madgwick 滤波器
filter.updateIMU(gyroX, gyroY, gyroZ, accX, accY, accZ);
// 获取计算出的姿态角 (弧度)
float roll = filter.getRoll();
float pitch = filter.getPitch();
float yaw = filter.getYaw();
// 打印到串口用于调试
Serial.print("Roll: "); Serial.print(roll * 180/M_PI);
Serial.print("\tPitch: "); Serial.print(pitch * 180/M_PI);
Serial.print("\tYaw: "); Serial.print(yaw * 180/M_PI);
Serial.println();
delay(10); // 控制循环频率
}
测试: 上传这段代码,打开串口监视器,当你慢慢倾斜无人机时,应该能看到 Roll 和 Pitch 值随之变化,转动无人机,Yaw 值也会变化,这是成功的第一步!
步骤 2:读取遥控器信号
你需要一个函数来读取 PWM 信号,通常使用 pulseIn() 函数。
#define CHAN1_PIN 2 // 副翼
#define CHAN2_PIN 4 // 升降
#define CHAN3_PIN 7 // 油门
#define CHAN4_PIN 8 // 方向
int chan1_val, chan2_val, chan3_val, chan4_val;
void readReceiver() {
chan1_val = pulseIn(CHAN1_PIN, HIGH, 25000); // 约 1-2ms 的脉宽
chan2_val = pulseIn(CHAN2_PIN, HIGH, 25000);
chan3_val = pulseIn(CHAN3_PIN, HIGH, 25000);
chan4_val = pulseIn(CHAN4_PIN, HIGH, 25000);
}
测试: 在 loop() 中调用 readReceiver() 并打印 chan3_val(油门),当你拨动摇杆时,应该能看到数值在 1000-2000 之间变化。
步骤 3:实现 PID 控制器
PID 控制器是无人机的“平衡大师”,它比较“目标值”(我们希望的角度,通常是0)和“当前值”(传感器读到的角度),然后计算出修正量。
#include <PID_v1.h>
// 定义 PID 对象
// PID(&输入值, &输出值, &设定值, Kp, Ki, Kd, 方向)
double rollInput, rollOutput, rollSetpoint = 0;
double pitchInput, pitchOutput, pitchSetpoint = 0;
double yawInput, yawOutput, yawSetpoint = 0;
// PID 参数,需要实际调试
double Kp_roll = 1.0, Ki_roll = 0.1, Kd_roll = 0.01;
double Kp_pitch = 1.0, Ki_pitch = 0.1, Kd_pitch = 0.01;
double Kp_yaw = 1.0, Ki_yaw = 0.01, Kd_yaw = 0.0;
// 创建 PID 实例
PID rollPID(&rollInput, &rollOutput, &rollSetpoint, Kp_roll, Ki_roll, Kd_roll, DIRECT);
PID pitchPID(&pitchInput, &pitchOutput, &pitchSetpoint, Kp_pitch, Ki_pitch, Kd_pitch, DIRECT);
PID yawPID(&yawInput, &yawOutput, &yawSetpoint, Kp_yaw, Ki_yaw, Kd_yaw, DIRECT);
void setupPIDs() {
rollPID.SetMode(AUTOMATIC);
pitchPID.SetMode(AUTOMATIC);
yawPID.SetMode(AUTOMATIC);
}
在 loop() 中,将传感器读到的角度赋值给 rollInput 和 pitchInput,然后调用 rollPID.Compute() 和 pitchPID.Compute() 得到 rollOutput 和 pitchOutput。
步骤 4:混合电机输出
这是最后一步,将 PID 输出、遥控器输入和基础油门混合,计算出最终给每个电机的 PWM 值。
// 电机引脚定义
#define M1_PIN 3 // 前左
#define M2_PIN 5 // 前右
#define M3_PIN 6 // 后左
#define M4_PIN 9 // 后右
// 电机基础油门
float base_throttle;
void mixOutput() {
// 1. 从遥控器获取基础油门
base_throttle = map(chan3_val, 1000, 2000, 1000, 1500); // 将遥控器油门映射到安全范围
// 2. 应用 PID 修正
// 电机布局:
// M1(FL) M2(FR)
// M3(BL) M4(BR)
// 横滚控制: 左右电机速度差
int roll_mix = rollOutput; // rollOutput 可能为正或负
// 俯仰控制: 前后电机速度差
int pitch_mix = pitchOutput; // pitchOutput 可能为正或负
// 偏航控制: 对角电机速度差
int yaw_mix = yawOutput;
// 计算每个电机的最终值
int motor1_val = base_throttle - pitch_mix + roll_mix + yaw_mix;
int motor2_val = base_throttle - pitch_mix - roll_mix - yaw_mix;
int motor3_val = base_throttle + pitch_mix + roll_mix - yaw_mix;
int motor4_val = base_throttle + pitch_mix - roll_mix + yaw_mix;
// 限制 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);
// 输出到电机
analogWrite(M1_PIN, motor1_val / 8 - 125); // 电调通常使用 1000-2000 的脉宽,analogWrite 是 0-255
analogWrite(M2_PIN, motor2_val / 8 - 125);
analogWrite(M3_PIN, motor3_val / 8 - 125);
analogWrite(M4_PIN, motor4_val / 8 - 125);
}
注意: analogWrite() 的输出范围是 0-255,而电调通常需要 1000-2000us 的 PWM 信号,上面的 motor_val / 8 - 125 是一个简单的转换(1000/8-125=0, 2000/8-125=125),但更可靠的方法是使用 servo.writeMicroseconds(),更好的做法是使用 Servo 库来控制电调。
第四部分:调试与安全(极其重要!)
绝对不要在室内或人群密集处进行首次试飞!
-
地面测试:
- 电机测试: 在不安装螺旋桨的情况下,通过遥控器给油门,观察四个电机是否按照你的预期旋转(M1逆时针,M2顺时针,M3顺时针,M4逆时针),如果方向反了,交换任意两根电机线即可。
- PID 调试: 这是最耗时也是最关键的一步。
- P (比例): 从一个很小的值开始(如 0.1),增加 P,无人机会开始“抖动”,抖动越厉害,P 越大,找到一个刚好能让无人机抵抗轻微晃动的值。
- D (微分): 增加 D 可以抑制抖动,让飞行更平滑,但如果 D 太大,会导致无人机响应迟钝甚至产生新的振荡。
- I (积分): 主要用于抵抗持续的、方向不变的力(如风),I 太大会导致无人机在平衡点附近来回“漂移”,初学者可以先设为 0。
- 推荐顺序: 先调俯仰和横滚的 PID,最后调偏航,偏航的 PID 通常比较简单。
-
首次试飞:
- 找一个巨大、开阔、无风的草地。
- 将无人机放在起飞点,机头对着你。
- 油门推到最低,然后快速推到中高位置(约 50%)。
- 无人机可能会剧烈晃动,不要惊慌,立即将油门拉回最低。
- 根据晃动的方向,微调相应的 PID 参数,然后再次尝试。
- 重复这个过程,直到无人机能短暂地悬停。
总结与进阶
- 这个项目涉及硬件、软件、物理和调试,挑战巨大,但完成后你会收获满满,从简单的 Arduino Mega + MPU-6050 开始,一步步调试,是成功的关键。
- 进阶:
- 使用气压计: 添加气压计(如 BMP280)可以实现定高飞行。
- 使用激光雷达: 激光雷达可以实现厘米级的精准定高和室内飞行。
- 姿态模式与自稳模式: 实现不同的飞行模式。
- 自动返航: 在遥控器信号丢失时,自动飞回起飞点。
- 开源固件: 当你对这个项目有了深入理解后,可以尝试研究和配置 Cleanflight/Betaflight 等更强大的固件。
祝你项目成功!这是一个充满乐趣和挑战的旅程。
