无人机双环PID悬停控制全解析

无人机双环PID悬停控制全解析

目录

1. 无人机悬停控制系统架构

2. 位置 PID 控制器设计

PID 结构体(扩展到位置控制)

3. 位置控制与姿态控制结合

位置数据结构

位置环 PID 初始化

4. 位置控制循环

5. 完整控制流程(位置 + 姿态 + 电机)

6. 传感器数据融合(GPS / 光流)

GPS 数据读取示例

光流数据读取示例

7. 调试与优化建议


我们在之前的 姿态控制 PID 基础上,增加 位置控制 PID 层,这样无人机就可以根据 GPS 或 光流模块 提供的位置信息,实现精准悬停。

我会给你一个 完整的位置 + 姿态双环 PID 控制方案,包括:

  1. 系统架构(位置环 → 姿态环 → 电机混控)
  2. 位置 PID 设计
  3. 传感器数据融合(GPS / 光流 → 位置估计)
  4. 代码实现(STM32 HAL 库)
  5. 调试与优化建议

1. 无人机悬停控制系统架构

悬停控制是一个 双环 PID 结构

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 位置环 PID │ │ 姿态环 PID │ │ 角速度环PID │ │ 电机混控 │ │ (X/Y/Z) │ │ (Roll/Pitch/Yaw) │ │ (ωx/ωy/ωz) │ │ (Motor1~4) │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 期望位置 │ │ 期望姿态角 │ │ 期望角速度 │ │ PWM信号输出 │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ 
  • 位置环:输入期望位置(如悬停点)和实际位置(来自 GPS / 光流),输出期望姿态角(Roll/Pitch)和期望高度(油门修正)。
  • 姿态环:输入期望姿态角和实际姿态角(来自 MPU-9250),输出期望角速度。
  • 角速度环:输入期望角速度和实际角速度(来自陀螺仪),输出电机控制量。
  • 电机混控:结合油门和姿态控制量,计算四个电机的 PWM 值。

2. 位置 PID 控制器设计

位置环需要对 X、Y、Z 三个方向分别控制:

  • X/Y 轴:控制无人机在水平面上的位置,输出期望的横滚角(Roll)和俯仰角(Pitch)。
  • Z 轴:控制无人机的高度,输出油门修正量。

PID 结构体(扩展到位置控制)

typedef struct { float Kp, Ki, Kd; float setpoint; float feedback; float error; float integral; float derivative; float prev_error; float output; float integral_limit; float output_limit; } PID_Controller; void PID_Init(PID_Controller *pid, float Kp, float Ki, float Kd, float integral_limit, float output_limit) { pid->Kp = Kp; pid->Ki = Ki; pid->Kd = Kd; pid->integral_limit = integral_limit; pid->output_limit = output_limit; pid->setpoint = 0.0f; pid->feedback = 0.0f; pid->error = 0.0f; pid->integral = 0.0f; pid->derivative = 0.0f; pid->prev_error = 0.0f; pid->output = 0.0f; } void PID_Update(PID_Controller *pid, float dt) { pid->error = pid->setpoint - pid->feedback; float P = pid->Kp * pid->error; pid->integral += pid->error * dt; if (pid->integral > pid->integral_limit) pid->integral = pid->integral_limit; if (pid->integral < -pid->integral_limit) pid->integral = -pid->integral_limit; float I = pid->Ki * pid->integral; pid->derivative = (pid->error - pid->prev_error) / dt; float D = pid->Kd * pid->derivative; pid->output = P + I + D; if (pid->output > pid->output_limit) pid->output = pid->output_limit; if (pid->output < -pid->output_limit) pid->output = -pid->output_limit; pid->prev_error = pid->error; } 

3. 位置控制与姿态控制结合

位置数据结构

typedef struct { float x; float y; float z; } Position; Position target_pos; // 期望位置 Position current_pos; // 当前位置(来自GPS/光流) 

位置环 PID 初始化

PID_Controller pid_pos_x, pid_pos_y, pid_pos_z; void Position_PID_Init(void) { // X轴位置PID(输出期望Roll角) PID_Init(&pid_pos_x, 2.0f, 0.1f, 0.05f, 50.0f, 15.0f); // 输出限幅±15° // Y轴位置PID(输出期望Pitch角) PID_Init(&pid_pos_y, 2.0f, 0.1f, 0.05f, 50.0f, 15.0f); // Z轴位置PID(输出油门修正量) PID_Init(&pid_pos_z, 5.0f, 0.2f, 0.1f, 100.0f, 200.0f); // 输出限幅±200us } 

4. 位置控制循环

void Position_Control_Loop(float dt) { // 更新位置环PID pid_pos_x.setpoint = target_pos.x; pid_pos_x.feedback = current_pos.x; PID_Update(&pid_pos_x, dt); pid_pos_y.setpoint = target_pos.y; pid_pos_y.feedback = current_pos.y; PID_Update(&pid_pos_y, dt); pid_pos_z.setpoint = target_pos.z; pid_pos_z.feedback = current_pos.z; PID_Update(&pid_pos_z, dt); // 位置环输出作为姿态环输入 pid_roll.setpoint = pid_pos_y.output; // Y轴位置误差→Roll角 pid_pitch.setpoint = pid_pos_x.output; // X轴位置误差→Pitch角 pid_yaw.setpoint = 0.0f; // 悬停时航向角保持0° // 油门修正 throttle = 1500.0f + pid_pos_z.output; // 基础油门1500us + Z轴修正 throttle = (throttle < 1000) ? 1000 : (throttle > 2000) ? 2000 : throttle; } 

5. 完整控制流程(位置 + 姿态 + 电机)

void Drone_Hover_Loop(void) { float dt = 0.01f; // 100Hz控制频率 // 1. 读取传感器数据 read_mpu9250_data(&euler, gyro); // 姿态角和角速度 read_position_sensor(&current_pos); // GPS/光流位置 // 2. 位置环控制 Position_Control_Loop(dt); // 3. 姿态环控制 pid_roll.feedback = euler.roll; pid_pitch.feedback = euler.pitch; pid_yaw.feedback = euler.yaw; PID_Update(&pid_roll, dt); PID_Update(&pid_pitch, dt); PID_Update(&pid_yaw, dt); // 4. 角速度环控制 pid_rate_roll.setpoint = pid_roll.output; pid_rate_pitch.setpoint = pid_pitch.output; pid_rate_yaw.setpoint = pid_yaw.output; pid_rate_roll.feedback = gyro[0]; pid_rate_pitch.feedback = gyro[1]; pid_rate_yaw.feedback = gyro[2]; PID_Update(&pid_rate_roll, dt); PID_Update(&pid_rate_pitch, dt); PID_Update(&pid_rate_yaw, dt); // 5. 电机混控 motor1 = throttle + pid_rate_roll.output - pid_rate_pitch.output - pid_rate_yaw.output; motor2 = throttle - pid_rate_roll.output - pid_rate_pitch.output + pid_rate_yaw.output; motor3 = throttle - pid_rate_roll.output + pid_rate_pitch.output - pid_rate_yaw.output; motor4 = throttle + pid_rate_roll.output + pid_rate_pitch.output + pid_rate_yaw.output; // 限幅 motor1 = (motor1 < 1000) ? 1000 : (motor1 > 2000) ? 2000 : motor1; motor2 = (motor2 < 1000) ? 1000 : (motor2 > 2000) ? 2000 : motor2; motor3 = (motor3 < 1000) ? 1000 : (motor3 > 2000) ? 2000 : motor3; motor4 = (motor4 < 1000) ? 1000 : (motor4 > 2000) ? 2000 : motor4; // 6. 输出到电机 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor1); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, motor2); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, motor3); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, motor4); } 

6. 传感器数据融合(GPS / 光流)

GPS 数据读取示例

void read_gps_data(Position *pos) { // 假设GPS模块通过串口输出NMEA数据 // 解析$GPGGA或$GPRMC帧,提取经纬度和高度 // 转换为局部坐标系(如ENU坐标系) pos->x = gps_lon_to_x(gps_lon); pos->y = gps_lat_to_y(gps_lat); pos->z = gps_alt; } 

光流数据读取示例

void read_optical_flow_data(Position *pos) { // 读取光流模块的X/Y位移和高度数据 pos->x += optical_flow_x * dt; pos->y += optical_flow_y * dt; pos->z = sonar_alt; // 超声波或气压计高度 } 

7. 调试与优化建议

  1. 参数整定顺序
    • 先调姿态环,再调位置环。
    • 位置环的 Kp 从小开始,逐步增加,避免振荡。
  2. 传感器数据滤波
    • GPS 数据噪声大,建议使用卡尔曼滤波或滑动平均。
    • 光流数据需结合高度信息进行缩放。
  3. 安全保护
    • 加入位置超范围保护(如偏离悬停点过远时自动返航)。
    • 低电压保护,避免失控。
  4. 仿真验证
    • 先用 MATLAB/Simulink 搭建仿真模型,验证位置控制逻辑。
  5. 实际测试
    • 先在室内用光流 + 超声波测试悬停,再到室外用 GPS 测试。

✅ 我已经给你一个 完整的位置 + 姿态双环 PID 悬停控制方案,可以直接在 STM32 上实现,支持 GPS 或光流模块。

如果你需要的话,我可以帮你 加上卡尔曼滤波(EKF)融合 GPS 和光流数据,这样位置估计会更稳定,尤其在 GPS 信号弱的环境下。

Read more

从零开始“养龙虾”:OpenClaw 本地极简部署与 QQ 机器人接入全保姆级教程

从零开始“养龙虾”:OpenClaw 本地极简部署与 QQ 机器人接入全保姆级教程

文章目录 * 引言 * 什么是 OpenClaw? * 为什么选择 OpenClaw? * 一、基础环境准备 * 1. 安装 Node.js (v22及以上) * 2.安装 Git * 3. 解决 npm 被拦截(没报错跳过) * 二、一键部署与唤醒“龙虾” * 1.全自动拉取与组装 * 2.醒龙虾与配置“大脑” * 三、接入官方 QQ 机器人(可选) * 1. 领取官方机器人的“身份证” * 2. 本地安装专属通信插件 * 3. 结果展示 * 总结 引言 什么是 OpenClaw? 最近开源界有一只“红皮小龙虾”非常火,它就是 OpenClaw。

iOS开发针对苹果新系统iOS26的兼容适配UITabBarButtonItem & UITabBar的液态玻璃效果/当前wifi ssid获取

1. UITabBarButtonItem液态玻璃效果         兼容处理:         第一种方式(不推荐):把所有的UITabBarButtonItem关闭液态玻璃效果: if (@available(iOS 26.0, *)) { self.navigationItem.rightBarButtonItem.hidesSharedBackground = YES; self.navigationItem.leftBarButtonItem.hidesSharedBackground = YES; } else { // Fallback on earlier versions }         第二种方式:所有导航栏按钮全部采用UITabBarButtonItem,支持液态玻璃效果。         第三种方式:降低Xcode版本到Xcode25及以下版本,然后再打包         第四种方式:使用兼容模式显示传统UI风格,也就是取消TabBar液态玻璃效果:         打开info.plist,添加一个Boolean键值对,取消液态玻璃效果,

OpenClaw安装和接入飞书机器人完整教程

OpenClaw安装和接入飞书机器人分三大部分组织回答: 1)先讲环境准备和OpenClaw基础安装(分阿里云和本地Windows两种场景); 2)再讲飞书机器人配置(包括应用创建、通道添加、事件订阅); 3)最后讲验证和配置AI模型。 为了更直观,在部署方式对比、配置项说明等地方用表格呈现。 这是一份完整的OpenClaw安装及接入飞书机器人的教程。将涵盖从环境准备、OpenClaw部署(含阿里云服务器和本地Windows两种方式)、AI模型(以阿里云百炼为例)配置,到最终在飞书开放平台创建并接入机器人的全流程。 第一部分:准备工作与核心认知 在开始动手前,我们需要先了解 OpenClaw 是什么,并准备好必要的账号和工具。 1.1 什么是 OpenClaw? OpenClaw(昵称“小龙虾”,曾用名 ClawdBot / Moltbot)是一个开源的个人AI智能体框架。它本身不具备推理能力,需要对接大语言模型(如阿里云百炼、七牛云、OpenAI等)的API。它的核心价值在于: * 真正的执行能力:能通过“技能”

一文吃透SBUS协议:从原理到实战(无人机/航模/机器人适用)

在无人机、航模、机器人等精密控制领域,“稳定、快速、可靠”是控制信号传输的核心诉求。传统的PWM信号虽然简单直观,但存在通道数有限、抗干扰能力弱、布线复杂等痛点。而SBUS(Serial Bus)协议——由FUTABA公司专为遥控设备设计的串行数字通信协议,凭借单线传输多通道数据、抗干扰强、延迟低的核心优势,逐渐成为行业主流。 本文将从“是什么-怎么工作-协议细节-厂家产品-接口设计-代码实现-实战技巧-常见问题”八个维度,用最通俗的语言+大量对比表格,全面拆解SBUS协议。无论你是刚入门的电子爱好者,还是需要落地项目的工程师,都能从本文中找到所需的实用信息。 一、SBUS协议基础认知:核心定位与优势对比 在深入技术细节前,我们先通过对比和基础定义,快速建立对SBUS的认知。很多人会把SBUS和常见的UART、PWM等混淆,这里先明确其核心定位:SBUS是基于反向电平UART的“应用层控制协议”,专门用于遥控器与接收机、接收机与飞控/执行器之间的控制信号传输。 1.1 为什么需要SBUS?传统方案的痛点 在SBUS出现之前,航模和早期无人机主要使用PWM或PPM协议传输控