睿诚科技协会

Arduino无人机源代码如何获取与使用?

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

Arduino无人机源代码如何获取与使用?-图1
(图片来源网络,侵删)
  1. 飞行控制器:这是无人机的“大脑和神经系统”,它负责实时处理传感器数据(陀螺仪、加速度计等),并通过 PWM 信号控制电机的转速,以维持飞行姿态或执行飞手指令。Arduino 非常适合担任这个角色,特别是对于入门级和 DIY 无人机。
  2. 机载计算机:这是无人机的“思考中枢”,它负责运行更复杂的算法,如自主飞行路径规划、计算机视觉(目标跟踪、避障)、与地面站的通信等,这个角色通常由性能更强的 Raspberry Pi (树莓派)NVIDIA Jetson 等设备担任。

Arduino 无人机源代码主要指的是飞行控制器的固件,下面我将为您分解这个固件的构成,并提供一个基于 Arduino 的、简化的多旋翼无人机源代码示例。


Arduino 无人机源代码的核心组成部分

一个成熟的 Arduino 无人机固件,无论多么复杂,都离不开以下几个模块:

  1. 传感器数据融合

    • IMU (惯性测量单元):这是最关键的传感器,包含陀螺仪(测量角速度)和加速度计(测量加速度/重力方向)。
    • MPU6050MPU9250 是最常用的 IMU 传感器。
    • 传感器数据通过 I2C 通信协议读取。
    • 由于陀螺仪有漂移,加速度计有噪声,需要使用卡尔曼滤波互补滤波算法将两者数据融合,得到准确、稳定的姿态(俯仰角、横滚角、偏航角)。
  2. 控制算法

    Arduino无人机源代码如何获取与使用?-图2
    (图片来源网络,侵删)
    • PID 控制器:这是无人机稳定飞行的灵魂。
    • P (Proportional - 比例):根据当前姿态与期望姿态的误差大小,成比例地进行修正,误差越大,修正力越大。
    • I (Integral - 积分):消除稳态误差,由于风的影响,飞机会持续向一边倾斜,P 控制无法完全修正,I 控制会累积这个持续的误差,并产生一个持续的修正力。
    • D (Derivative - 微分):抑制震荡,提高响应速度,它根据误差变化率来预测未来的误差,并进行提前修正,防止过冲。
    • 需要为俯仰、横滚、偏航三个轴分别设置 PID 参数。
  3. 电机控制

    • 使用 Arduino 的 analogWrite() 函数(或更高效的定时器库)生成 PWM (脉冲宽度调制) 信号。
    • PWM 信号控制连接到电子调速器 的引脚,ESC 再控制无刷电机的转速。
    • 四旋翼无人机的油门输出通常是四个电机 PWM 值的合成,以实现升降、前后、左右、旋转等动作。
  4. 遥控信号接收

    • 飞手通过遥控器发送指令(油门、俯仰、横滚、偏航)。
    • Arduino 通过读取PPM 信号或多个PWM 信号来获取这些指令,对于 PPM 信号,通常使用一个中断引脚来解码。
  5. 通信与调试

    • 通过 Serial 串口输出传感器数据、PID 输出、电机值等,方便在 Arduino IDE 的串口监视器中进行调试。

简化的 Arduino 四旋翼无人机源代码示例

以下是一个功能非常基础的示例,旨在演示核心控制逻辑。它不能直接用于飞行,因为它缺少了安全特性(如失控保护、失控自动降落等),PID 参数需要大量调试才能稳定。

Arduino无人机源代码如何获取与使用?-图3
(图片来源网络,侵删)

这个例子假设您已经:

  • 连接好了 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.csrc/main/sensors/ 目录下的代码,理解其实现思想。

ArduPilot (支持 Arduino 和更高级平台)

ArduPilot 是一个功能极其强大的开源飞控项目,支持固定翼、多旋翼、直升机等多种机型,它有专门的 ArduCopter(多旋翼)固件。

  • 特点
    • 功能全面,支持自主飞行、RTL(返航)、定高、定点等高级功能。
    • 有非常活跃的社区和详尽的文档。
    • 支持 Arduino (如 Mega 2560), 但也支持 Pixhawk 等性能更强的硬件。
  • 获取源码

如果你想在 Arduino 上实现一个功能完整的无人机,直接基于 ArduPilot 进行二次开发是最好的选择。

MultiWii (经典入门级 Arduino 飞控)

MultiWii 是最早将 Arduino 用于 DIY 无人机的项目之一,非常经典,现在已经不太活跃,但其代码结构清晰,非常适合初学者学习。

  • 特点
    • 代码结构简单,易于理解。
    • 完全基于 Arduino (ATmega/AVR)。
    • 是学习飞控原理的绝佳教材。
  • 获取源码

总结与建议

  1. 从学习开始:如果你是初学者,不要急于写代码,先理解传感器融合PID控制的原理。
  2. 从示例入手:使用上面提供的简化代码,搭建一个最小的测试平台(比如用电机测试台),在确保安全的情况下,尝试调试 PID 参数,观察电机响应。
  3. 借鉴成熟项目:直接从零写一个稳定可靠的飞控固件难度极高,强烈建议研究 ArduPilotMultiWii 的源码,理解其架构和实现,然后在此基础上进行修改和扩展。
  4. 安全第一:无人机是高速旋转的设备,非常危险,在调试时,务必使用螺旋桨保护罩,并确保在空旷、安全的环境下进行测试。

希望这份详细的指南能帮助你开启 Arduino 无人机的开发之旅!

分享:
扫描分享到社交APP
上一篇
下一篇