☰
69元自制ESP32四旋翼:从物料选型到飞控算法全解析
2026/10/19 6:43:45 网站建设 项目流程

1. 从一张物料清单说起:69元预算到底花在了哪

先把账算清楚,这是整个项目能不能成立的前提。很多人一听"69元做四旋翼"第一反应是"不可能",但如果你把"无人机"这三个字拆开看——它本质上就是四个电机加一个能实时跑控制算法的单片机,剩下的都是外围。真正贵的东西从来不是飞行本身,而是图传、云台、GPS、长续航电池这些"附加价值"。我们这次要做的是一台能定高、能定点、能用手机Web页面遥控的迷你机,把这些附加项全部砍掉,成本自然就下来了。

我这台机器的物料清单大致是这样分配的:主控用ESP32模组,这是整个方案的核心,它自带Wi-Fi和蓝牙,意味着遥控链路不需要额外的接收机,手机直接连它的热点就能发指令,这一项就省掉了传统航模里遥控器加接收机的大几百块。四个空心杯电机配四支小桨,选7mm或8mm规格的,单套下来十几块钱。机架用3D打印或者干脆用碳纤维方管加尼龙扎带手工搭,成本压到个位数。电池用一块小容量1S锂电,配合一个微型升压模块给ESP32供电。再加上一颗六轴IMU、几个MOS管做电机驱动、若干电阻电容和洞洞板,整体就齐了。

这里有个关键取舍需要说清楚:为什么用空心杯电机而不是无刷电机?无刷效率高、寿命长,但每一个都要配独立的电调,四个电调加起来就超过整机预算了。空心杯电机虽然寿命短、效率低,但它的驱动极其简单——一个MOS管加一个续流二极管就能调速,用PWM直接控制占空比即可。对于一台以"验证飞控算法"为目的的迷你机来说,空心杯是完全合理的选择。你要清楚自己做这台机器的目标是什么:如果是想拍航拍视频,那这个方案不适合你;如果是想搞懂四旋翼到底怎么飞起来的、PID怎么调、姿态解算怎么回事,那这台机器是最低成本的入口。

提示:空心杯电机分有刷和无刷两种叫法容易混淆,这里说的"空心杯"指的是有刷直流电机,它的转子是杯状绕组,转动惯量小、响应快,非常适合小尺寸四旋翼。购买时注意看额定电压和空载转速,一般选3.7V下转速在40000转左右的型号。

物料选型里还有一个容易被忽略的点:MOS管的选型。很多新手随便拿一个IRF540就往上焊,结果发现电机转起来软绵绵的。原因是IRF540的导通阈值电压偏高,而ESP32的GPIO输出是3.3V电平,根本没法让它完全导通,MOS管工作在线性区,压降大、发热严重。正确的做法是选逻辑电平MOS管,比如AO3400或者SI2302这类,它们的导通阈值在1.5V到2.5V之间,3.3V驱动绰绰有余。这个细节我在第一次做的时候踩过,电机转速上不去,查了半天以为是PWM频率问题,最后用万用表一量MOS管漏源压降,才发现是管子选错了。

2. ESP32做主控:为什么不是STM32,也不是树莓派

2.1 无线链路内置带来的架构简化

传统四旋翼的遥控链路是这样的:遥控器发射2.4G信号,机载接收机解码后输出PPM或SBUS信号给飞控,飞控再解析成油门、俯仰、横滚、偏航四个通道。这条链路里,接收机是独立硬件,遥控器是独立硬件,两者还要对频。而ESP32直接把Wi-Fi做在了芯片里,手机连上ESP32开的热点,通过WebSocket或者HTTP就能把控制指令发过来,整个接收机环节直接消失。

这个架构变化带来的不只是省钱,更重要的是调试效率的提升。传统方式下你想看飞控内部状态,要么接数传电台,要么飞完拔下SD卡看日志。而ESP32方案里,你可以直接在Web页面上实时显示姿态角、电机输出、电池电压,甚至可以在页面上放几个滑块直接调PID参数,改完立刻生效,不用反复烧录固件。我在调姿态环的时候,就是一边飞一边在手机上拖PID滑块,看着波形调,效率比传统方式高太多。

2.2 双核带来的任务划分思路

ESP32有两个核心,这在飞控场景下是个巨大的优势。四旋翼的控制循环对实时性要求很高,姿态解算和PID计算必须稳定在固定频率执行,一般至少500Hz,最好1kHz。如果Wi-Fi通信和飞控跑在同一个核心上,Wi-Fi协议栈的中断会打断控制循环,导致控制周期抖动,飞行表现就是电机声音忽高忽低、姿态发飘。

合理的做法是把控制循环绑在一个核心上,Wi-Fi和Web服务跑在另一个核心上。ESP32的Arduino核心支持xTaskCreatePinnedToCore来指定任务运行的核心。控制任务用最高优先级,Wi-Fi任务用较低优先级。两个核心之间通过队列或者全局变量加互斥锁来交换数据。这样即使Wi-Fi那边在忙着处理HTTP请求,控制循环依然能稳定运行。

// 控制任务绑定到核心1,优先级最高 xTaskCreatePinnedToCore( controlLoopTask, // 任务函数 "ControlLoop", // 任务名 4096, // 栈大小 NULL, // 参数 configMAX_PRIORITIES - 1, // 最高优先级 NULL, // 任务句柄 1 // 绑定到核心1 );

2.3 控制频率与PWM输出的实际约束

ESP32的LEDC外设可以输出PWM,但它的通道数量有限,而且和Servo库、Tone库有冲突。四个电机需要四路PWM,频率一般选在20kHz以上,避免进入人耳可听范围产生啸叫。空心杯电机对PWM频率的容忍度比较高,但频率太低会导致电机发热和转矩脉动。我实测下来,20kHz到25kHz是比较合适的区间,既听不到啸叫,MOS管开关损耗也可以接受。

这里有个坑:ESP32的LEDC在Arduino核心里的默认分辨率是8位,也就是占空比0到255。如果你用ledcSetup设置频率为20kHz,分辨率会自动降到8位以下,因为LEDC的时钟源频率有限,频率和分辨率是此消彼长的关系。具体来说,80MHz的时钟源下,20kHz频率最多能支持12位分辨率,但Arduino核心可能会限制你。实际使用中8位分辨率对电机控制来说够用,但如果想做更精细的油门曲线,可以考虑用RMT外设或者直接操作寄存器。

3. IMU校准:从原始数据到可信姿态角

3.1 为什么必须校准,不校准会怎样

IMU是四旋翼的"内耳",它告诉飞控现在是平着的还是歪着的。但IMU芯片出厂时的零偏和灵敏度误差是存在的,而且焊接时的应力、电路板的形变都会让误差进一步变化。如果不校准,最直接的后果就是:你明明把飞机放在水平桌面上,飞控却认为它在往右倾斜,于是自动往左修正,一起飞就往一边冲。

校准分两步:零偏校准和尺度校准。零偏校准是让IMU静止放置,采集几百个样本取平均,得到陀螺仪和加速度计的零偏值,之后每次读取都减去这个零偏。尺度校准更复杂一些,需要把IMU放在已知的多个姿态下,对比输出值和理论值,计算灵敏度系数。对于消费级MEMS IMU来说,零偏校准是必须的,尺度校准在要求不高的场景下可以省略,因为芯片出厂时已经做过一次尺度校准,误差通常在1%以内。

3.2 加速度计校准的六面法实操

加速度计校准最经典的方法是六面法:把IMU分别朝上、朝下、朝前、朝后、朝左、朝右六个方向静止放置,每个方向采集一段时间的数据。理论上,朝上时Z轴应该输出+1g,朝下时输出-1g,其他轴输出0。实际采集到的值和理论值的差异,就反映了零偏和尺度误差。

具体操作时,我会在代码里写一个校准模式,通过串口或者Web页面触发。进入校准模式后,提示用户把飞机放到第一个面,稳定后按确认,采集500个样本取平均,然后进入下一个面。六个面都采集完后,用最小二乘法解出零偏和尺度矩阵。这个过程听起来麻烦,但做一次就行,而且校准结果可以存在ESP32的NVS里,下次上电直接读取。

// 简化的六面法校准数据采集 float accelBias[3] = {0, 0, 0}; float accelScale[3] = {1, 1, 1}; // 假设六个面的理论值分别是 const float refVals[6][3] = { {0, 0, 1}, // 朝上 {0, 0, -1}, // 朝下 {0, 1, 0}, // 朝前 {0, -1, 0}, // 朝后 {1, 0, 0}, // 朝右 {-1, 0, 0} // 朝左 };

3.3 陀螺仪零偏校准的温度陷阱

陀螺仪零偏校准比加速度计简单,静止采集取平均就行。但这里有个容易被忽略的问题:陀螺仪零偏会随温度漂移。你刚上电时校准的零偏,飞了两分钟后电机发热、电路板温度升高,零偏可能已经变了。表现就是飞机悬停时慢慢往一个方向转,你不动摇杆它自己偏航。

解决思路有两个:一是上电后先等一会儿,让电路板温度稳定再校准;二是飞行中做在线零偏估计,当检测到飞机处于静止状态时(加速度计输出接近重力、陀螺仪输出变化很小),自动更新零偏。第二种方法更优雅,但实现复杂度高一些。我在实际项目里用的是折中方案:上电后延时30秒再校准,同时限制单次飞行时间不超过5分钟,基本能避开温度漂移最严重的阶段。

注意:校准的时候一定要把飞机放在真正水平的面上。我见过有人放在床上校准,床垫是软的,飞机放上去其实有倾斜,校准出来的结果就是错的。用水平仪确认一下,或者干脆放在地砖上。

4. MAVLink协议适配:让地面站认识你的飞机

4.1 MAVLink到底解决了什么问题

MAVLink是一个轻量级的通信协议,最初是为无人机设计的,现在已经成为行业事实标准。它的核心价值在于定义了一套通用的消息格式,让不同厂商的飞控和地面站之间能互相通信。你可以在Mission Planner或者QGroundControl里看到飞机的姿态、位置、电池状态,也可以从地面站发送指令让飞机起飞、降落、飞往某个航点。

对于这台自制四旋翼来说,适配MAVLink的意义在于:你不需要自己写地面站软件,直接用现成的开源地面站就能监控和调试。而且MAVLink的消息定义是公开的,你可以按需实现自己需要的消息子集,不用全部实现。比如姿态消息ATTITUDE、心跳消息HEARTBEAT、系统状态消息SYS_STATUS,这几个是基础,实现了就能在地面站里看到飞机的基本状态。

4.2 在ESP32上实现MAVLink的最小集

MAVLink的消息是二进制格式,每个消息有固定的帧头和校验。在ESP32上实现MAVLink有两种方式:一是用官方的MAVLink库生成C头文件,二是自己手写打包和解包函数。官方库功能全但体积大,对于ESP32这种资源有限的平台,我倾向于只实现需要的几个消息,手写打包函数。

以HEARTBEAT消息为例,它的格式是固定的:帧头、系统ID、组件ID、消息ID、载荷长度、载荷数据、校验。载荷里包含类型、自动驾驶仪类型、基模式、自定义模式、系统状态等字段。你只需要按字节顺序填充这些字段,然后计算CRC校验追加到末尾就行。

// 简化的MAVLink HEARTBEAT打包示例 void sendHeartbeat() { uint8_t buf[32]; uint8_t idx = 0; buf[idx++] = 0xFE; // 帧头 buf[idx++] = 9; // 载荷长度 buf[idx++] = 0; // 序列号 buf[idx++] = 1; // 系统ID buf[idx++] = 1; // 组件ID buf[idx++] = 0; // 消息ID,HEARTBEAT是0 // 载荷 buf[idx++] = 2; // 类型:四旋翼 buf[idx++] = 3; // 自动驾驶仪类型 buf[idx++] = 0; // 基模式 buf[idx++] = 0; // 自定义模式 buf[idx++] = 4; // 系统状态:活跃 // 版本和校验省略,实际使用需要完整实现 }

4.3 消息发送频率与带宽的平衡

MAVLink消息不能发太快,否则会占满Wi-Fi带宽,影响控制指令的实时性。但也不能太慢,否则地面站显示会卡顿。我的经验是:姿态消息10Hz到20Hz,心跳消息1Hz,系统状态消息2Hz,这样既能保证地面站显示流畅,又不会给Wi-Fi带来太大压力。

这里有个细节:MAVLink消息可以通过UDP发送,也可以通过串口发送。ESP32方案里用UDP比较方便,地面站监听一个UDP端口,ESP32往那个端口发消息就行。但UDP是不可靠的,消息可能丢失,对于监控用途来说可以接受,因为下一帧很快就会来。如果是发送控制指令,那就要考虑用TCP或者加确认机制。

5. 定高与定点:从能飞到飞得稳的关键跨越

5.1 定高为什么比想象中难

定高的原理听起来很简单:用气压计或者超声波测高度,高度低了就加油门,高了就减油门。但实际做起来,气压计受气流影响极大,电机一转,桨叶产生的气流就会让气压计读数跳动,飞机自己都分不清是真的升高了还是气压计被吹了。

我的解决方案是:气压计加海绵罩做物理滤波,同时软件上做互补滤波,把加速度计的积分和气压计的高度融合。加速度计短期精度高但会漂移,气压计长期稳定但短期噪声大,两者互补正好。具体实现时,用加速度计积分得到速度,再用气压计的高度修正速度估计,形成一个闭环。

// 互补滤波估计高度和垂直速度 float estAlt = 0, estVel = 0; float alpha = 0.98; // 加速度计权重 void updateAltitude(float accelZ, float baroAlt, float dt) { // 加速度计积分 estVel += (accelZ - 1.0) * dt; // 减去重力 estAlt += estVel * dt; // 气压计修正 float error = baroAlt - estAlt; estAlt += (1 - alpha) * error; estVel += (1 - alpha) * error / dt; }

5.2 定点飞行需要什么样的传感器

定点比定高更难,因为它需要知道水平方向的位置。没有GPS的情况下,定点只能靠光流传感器或者视觉里程计。光流传感器通过向下拍摄地面图像,计算图像移动来推算水平速度,成本比GPS低,室内也能用。但光流对地面纹理有要求,纯色地面或者反光地面效果很差。

对于这台69元的机器,我的建议是:先做好定高,定点可以后续扩展。如果一定要做定点,可以考虑加一个便宜的光流模块,但要做好心理准备,调试工作量不小。光流的数据需要和IMU融合,还要处理光照变化、地面纹理变化带来的噪声,不是接上就能用的。

5.3 高度控制的PID整定经验

高度环的PID整定和姿态环不太一样。姿态环的响应要快,高度环的响应可以慢一些,因为高度变化本身就不需要那么快。P值给大了会震荡,飞机一上一下地跳;P值给小了响应慢,你推油门它半天不升。

我的经验是:先把P值从小往大调,直到飞机能稳定悬停但有轻微震荡,然后加D值抑制震荡。I值用来消除稳态误差,但不要给太大,否则会有积分饱和,飞机在高度变化时会有延迟。实际调的时候,我是在Web页面上放三个滑块,一边飞一边调,看着高度曲线调,比盲调快得多。

6. 手机Web遥控:不用装App的操控方案

6.1 为什么选Web而不是App

做手机遥控有很多方案:写一个安卓App、用蓝牙手柄、用Web页面。我选Web页面的理由很简单:跨平台、不用安装、改起来快。你只要打开手机浏览器,输入ESP32的IP地址,就能看到控制界面。安卓、iOS、电脑都能用,不用为每个平台单独开发。

Web页面的控制指令通过WebSocket发送,比HTTP轮询延迟低得多。WebSocket建立连接后,手机和ESP32之间保持一个长连接,手机端检测到摇杆移动就立刻发消息,ESP32收到后更新目标姿态。实测下来,局域网内WebSocket的延迟在10ms以内,对于手动飞行来说完全够用。

6.2 摇杆数据的采集与映射

Web页面上的摇杆可以用HTML5的触摸事件或者鼠标事件实现。触摸事件在手机上更自然,鼠标事件在电脑上更方便。我写了一个兼容两者的处理函数,触摸优先,没有触摸就用鼠标。

摇杆数据需要映射到四个通道:油门、偏航、俯仰、横滚。油门是0到100%,其他三个通道是-100%到+100%。映射的时候要注意死区处理,摇杆在中间位置附近的小抖动要忽略,否则飞机会有细微的漂移。

// 摇杆数据处理 function processJoystick(x, y) { // 死区处理 const deadzone = 0.05; if (Math.abs(x) < deadzone) x = 0; if (Math.abs(y) < deadzone) y = 0; // 映射到-100到100 const roll = x * 100; const pitch = y * 100; // 通过WebSocket发送 ws.send(JSON.stringify({ roll: roll, pitch: pitch, yaw: yawValue, throttle: throttleValue })); }

6.3 失控保护与通信超时处理

Web遥控最大的风险是通信中断。手机没电了、Wi-Fi信号弱了、浏览器卡死了,任何一种情况都可能导致飞机失控。所以失控保护是必须做的:ESP32端检测WebSocket连接状态,如果超过一定时间没收到指令,就自动进入降落模式,慢慢降低油门直到落地。

这个超时时间不能太短,否则网络抖动一下飞机就往下掉;也不能太长,否则真失控了飞机还在天上飞。我的设置是500ms,也就是半秒没收到指令就触发保护。实际测试中,正常飞行时指令间隔在20ms左右,500ms的超时阈值有足够的余量。

提示:调试阶段建议把桨叶拆掉,只观察电机响应。确认控制逻辑正确后再装桨叶试飞。我第一次试飞的时候没拆桨,结果摇杆方向映射反了,飞机直接翻了个跟头,桨叶打断了两根。

7. 整机调试与试飞:那些文档里不会写的事

7.1 电机转向与桨叶匹配

四旋翼有四个电机,对角两个转向相同,相邻两个转向相反。转向错了飞机直接翻,桨叶装反了效率极低。判断转向的方法很简单:装桨叶之前先给电机通电,用手指轻轻碰一下电机轴,感受转向。或者贴一小片胶带在电机轴上,通电看胶带往哪边转。

桨叶分正桨和反桨,外观很像但叶片角度相反。正桨装在逆时针转的电机上,反桨装在顺时针转的电机上。装错了的话,桨叶产生的气流方向是反的,升力大幅下降。我一般会在桨叶上做个标记,正桨画个红点,反桨画个蓝点,装机的时候不容易搞混。

7.2 振动对IMU的影响与减震处理

空心杯电机的振动比无刷电机大得多,而IMU对振动极其敏感。振动会让加速度计输出噪声增大,姿态解算出来的角度抖动,飞机飞起来像在发抖。减震的方法有几种:一是用软胶垫把IMU和机架隔离开,二是给IMU加低通滤波,三是做桨叶动平衡。

我用的是软胶垫加低通滤波的组合。软胶垫用那种很软的硅胶,厚度2mm左右,裁成小块垫在IMU板下面。低通滤波在代码里实现,截止频率设在50Hz左右,既能滤掉电机振动的高频成分,又不会引入太大延迟。

7.3 第一次试飞的检查清单

试飞前一定要逐项检查,我列了一个清单,每次试飞前都过一遍:

  • 电池电压是否充足,一般1S锂电充满是4.2V,低于3.5V就不要飞了
  • 桨叶是否装紧,转向是否正确
  • IMU校准是否完成,校准后有没有重新上电
  • 失控保护是否生效,可以模拟断开Wi-Fi测试
  • 飞行区域是否空旷,周围有没有人和障碍物
  • 遥控器(手机)电量是否充足

第一次试飞建议在室内无风环境下进行,高度控制在半米以内,主要观察飞机能不能稳定悬停、有没有自旋、响应方向是否正确。确认没问题后再逐步增加高度和飞行时间。

7.4 常见问题排查表

现象可能原因排查方法
一起飞就翻电机转向错误、桨叶装反、IMU校准错误检查转向和桨叶,重新校准IMU
悬停时自旋陀螺仪零偏未校准、偏航PID参数不对重新校准陀螺仪,调偏航P值
高度忽高忽低气压计受气流干扰、高度PID震荡给气压计加海绵罩,降低高度P值
响应迟钝控制频率太低、Wi-Fi延迟大检查控制循环频率,优化WebSocket
飞行时间短电池容量小、电机效率低换更大容量电池,检查桨叶匹配

8. 后续可以怎么玩:从能飞到飞得好

这台机器的定位是入门和验证,飞控算法跑通了、基本飞行稳定了,后面还有很多可以折腾的方向。比如把定高做好之后,可以试试简单的航点飞行,在地面站上点几个点,让飞机自己飞过去。虽然精度不会太高,但能跑通整个流程本身就是很有成就感的事。

另一个方向是加传感器。光流模块做室内定点,超声波做低空定高,甚至加一个小的激光测距模块做精准高度控制。每加一个传感器,就多一层数据融合的工作,但飞行的稳定性也会上一个台阶。

还有就是控制算法的升级。现在用的是经典PID,可以试试LQR或者模型预测控制,虽然在这台小机器上效果不一定明显,但作为学习平台是很好的练手项目。ESP32的算力跑简单的LQR还是够的,复杂一点的就要考虑换更强的处理器了。

我在这个项目里最大的体会是:四旋翼的难点从来不是让电机转起来,而是让四个电机协调地转。姿态解算、PID控制、传感器融合,这些才是真正花时间的地方。69元的硬件成本只是入场券,真正值钱的是你在调试过程中积累的那些经验——为什么这个参数要这样设、为什么那个现象是正常的、出了问题从哪里开始查。这些东西,文档里不会写,只有自己飞过、炸过、修过,才能真正理解。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询