编程实现控制FPV舵机云台

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 1 Likes: 0 Comments: 0

311.6、编程实现控制 FPV 舵机云台

#include <Servo.h>                     // 引入 Arduino Servo 库,获得舵机对象和控制接口

Servo myservo;                         // 创建第一个舵机对象,对应云台的左右旋转轴
Servo myservo2;                        // 创建第二个舵机对象,对应云台的上下俯仰轴

int pos = 0;                           // 保存本轮要写给舵机的角度值,初始值为 0

void setup() {                         // 上电或复位后只执行一次的初始化函数
  myservo.attach(9);                   // 把第一个舵机对象关联到数字引脚 D9
  myservo2.attach(10);                 // 把第二个舵机对象关联到数字引脚 D10
}                                      // setup() 初始化结束

void loop() {                          // 主循环会在开发板运行期间不断重复
  for (pos = 0; pos <= 180; pos += 1) { // 让 pos 从 0 逐次增加到 180,包含两个端点
    myservo.write(pos);                // 把当前角度目标写给第一个舵机
    myservo2.write(pos);               // 把同一个角度目标写给第二个舵机
    delay(15);                         // 每增加 1° 等待 15 ms,给舵机留出运动时间
  }                                    // 结束 0° 到 180° 的递增循环
  for (pos = 180; pos >= 0; pos -= 1) { // 让 pos 从 180 逐次减小到 0,包含两个端点
    myservo.write(pos);                // 反向阶段只向第一个舵机写入角度;这是视频最终代码的原样行为
    delay(15);                         // 每减小 1° 等待 15 ms
  }                                    // 结束 180° 到 0° 的递减循环
}                                      // 一轮结束后返回 loop() 开头继续运行

一、编程控制 SG90 舵机

本节先打开 Arduino Servo 库自带的 Sweep 示例,用 D9 测试云台的左右轴;随后在示例上增加第二个 Servo 对象,把上下轴信号接到 D10,并把修改后的草图烧录到 UNO。页首代码是视频最终实际编译、上传的版本,仅替换了原英文注释并逐行补充中文说明,没有擅自补写视频中缺少的控制语句。

1.1 舵机控制原理

普通位置式 RC 舵机有电源、地和信号三根线。控制器在信号线上周期性发送窄高电平脉冲,舵机内部控制电路根据**高电平持续时间(脉宽)**确定目标位置。课程把它称为 PWM 控制,这种说法在舵机应用中很常见;但它不是通过 analogWrite() 改变普通方波占空比来直接驱动电机,真正表达位置的是每帧脉冲的宽度。

视频课件给出的简化关系如下:

高电平脉宽课件标注角度精校说明
0.5 ms0°课件示意值,不应直接当成所有 SG90 兼容品的安全端点
1.0 ms45°按课件关系递增
1.5 ms90°通常用作中位的示意值
2.0 ms145°课件原图如此标注,但与其余等间隔关系不一致;线性示意应为 135°,145°应视为课件笔误
2.5 ms180°课件示意值,真实舵机未必能无碰撞地到达该端点

课件还把重复周期画成 20 ms,即约 50 Hz。当前 Arduino Servo 库的 AVR 实现也把刷新间隔定义为 20000 µs,但 attach(9) 的默认角度映射并不是课件中的 500~2500 µs,而是把 0~180 映射到默认的 544~2400 µs。attach(pin, min, max) 才能另外指定端点脉宽。脉宽、真实角度和机械极限会因舵机个体、兼容品和安装齿位而变化,所以程序中的“90°”是命令目标,不是经过传感器测得的绝对平台角度。

视频右侧的方向图把下轴的 0°/180°画成右/左,把上轴的 0°/180°画成上/下。这只是课程装配方向下的参考;舵机安装朝向、舵盘齿位或固定端改变后,肉眼看到的方向可能相反。

1.2 编程实现舵机控制

1.2.1 打开编程软件
1.2.1.1 示例

视频使用 Arduino IDE 2.0.1,先选择 Arduino Uno,再依次打开“文件 → 示例 → Servo → Sweep”。原始笔记中的“server”“sweet”和“历程”均为识别错误,正确名称分别是库名 Servo、示例名 Sweep 和“例程”。

Sweep 是 Servo 库附带的示例草图:它先把一个舵机关联到 D9,再令角度变量从 0 增到 180、从 180 减回 0。视频后半段直接在这份示例上增加第二只舵机,没有新建独立工程。

1.2.2 代码分析
代码初学者应理解的含义
#include <Servo.h>引入 Servo 库的声明;它不是把舵机接上电源,也不会自动识别舵机型号
Servo myservo;以 Servo 类型创建一个软件对象;对象名是 myservo,不是 myServer
int pos = 0;只把循环变量初始化为 0;这一行本身不会让机械轴立即转到物理零位
attach(pin)把一个舵机对象关联到指定数字信号脚,并开始由库生成控制脉冲
write(pos)写入目标角度;Servo 库负责把 0~180 的角度值换算为脉宽
delay(15)让程序在本次 1°步进后等待 15 ms;它不是把舵机信号周期改成 15 ms

两个 for 循环都包含首尾端点,因此每个方向各执行 181 次。忽略少量指令开销,一个方向约为 181 × 15 ms = 2.715 s,完整往返约为 5.43 s。这个数值表示程序更新目标角度的节奏,不表示舵机一定已经在每个 15 ms 间隔内准确到位;真实速度还取决于供电、负载和舵机性能。

代码中的两次 write() 是先后执行的,不是同一个 CPU 时钟瞬间发生。不过两条语句的时间差相对舵机的机械响应很小,肉眼会看到两轴近似同时开始变化。Servo 库还会在一个刷新周期内依次安排各通道脉冲,因此“同步”在这里应理解为使用相同的角度序列,而不是两路脉冲完全同相。

1.2.3 板子原理图

Arduino UNO R3 上带 ~ 标记的 D3、D5、D6、D9、D10、D11,确实是六个可由 analogWrite() 输出硬件 PWM 的数字引脚。但视频由此得出“控制舵机必须使用这六个引脚”的结论并不正确。

在本节所用的 AVR UNO 和当前官方 Servo 库中,舵机脉冲由 16 位 Timer1 的中断配合普通数字输出生成;attach(pin) 会把传入的数字脚设为输出,并不要求该脚带 ~。所以选 D9、D10 是示例和本项目的具体分配,不是 Servo 库的硬性限制。选择其他有效数字脚时仍要避开项目已经占用的功能,例如 D0/D1 通常用于串口。

还要注意一个容易混淆的反向影响:在经典 AVR UNO 上启用 Servo 库后,Timer1 被舵机控制占用,D9、D10 原有的 analogWrite() PWM 功能会受影响;当所有 Servo 对象都 detach() 后,这两脚才可恢复给普通 PWM 使用。这不妨碍 D9、D10 作为本节的舵机信号脚。

1.2.4 编程控制舵机

视频先只测试下方左右轴:myservo.attach(9) 将该舵机的信号线关联到 D9。attach() 中的 9 是开发板数字引脚编号,不是舵机编号,也不是“第 9 个 PWM 通道”。

首次通电时,舵盘可能立即向当前控制目标转动。第 310 节的机械组装没有先完成电气居中,因此应扶稳机构、让手指和线缆离开转轴,并准备在碰撞、堵转、持续嗡鸣或异常发热时立即断电。直接测试完整 0~180°只能证明视频这样操作过,不能证明这个范围对每一套云台都安全。

1.2.5 舵机接线

视频中两只舵机采用黄色、红色、棕色三芯线,实际接线为:

云台轴信号线电源线地线
下方左右轴黄色 → D9红色 → 课程板扩展口 5V棕色 → 课程板扩展口 GND
上方俯仰轴黄色 → D10红色 → 课程板扩展口 5V棕色 → 课程板扩展口 GND

接线前必须以舵机插头标识、实物资料和扩展口丝印为准,不能把颜色当作所有品牌通用的绝对标准。尤其不能只凭视频口述的“某一排”判断课程板扩展口:画面中的扩展焊点是这块教学板的定制接法,先用丝印或万用表确认 5V/GND,再断电插线。

舵机的红线和棕线是负载供电回路,不能接到普通数字 I/O 脚取电。视频通过 USB 给 UNO 供电,并把两只舵机接到板载 5V/GND 扩展口,这能复现课堂演示,但增加焊点不会增加 5V 电源的供电能力。两只舵机启动、换向或受阻时可能引起压降、UNO 复位和舵机抖动。TowerPro 对正品 SG90 Analog 标出的工作电压为 4.8 V,并建议通过外部适配器供电;课程中的兼容舵机仍应以自身规格为准。使用独立稳压电源时,电源容量应满足负载需求,而且外部电源 GND 必须与 UNO GND 共地,否则 D9/D10 的信号没有共同参考。

1.2.6 烧录代码

视频中的操作顺序是:

  1. 断电完成接线并检查 D9、5V、GND 没有插反。
  2. 用 USB 连接 UNO,在 IDE 中选择 Arduino Uno 和当时出现的 COM3。
  3. 点击上传,等待编译和写入完成。
  4. 第一次单舵机测试时,视频确认下方舵机从 0 递增到 180,再从 180 递减到 0。

COM3 只是录制电脑当时分配的端口号,不是项目固定配置;换电脑、USB 插口或驱动后可能显示为其他 COM 号。上传成功也只证明程序能够编译并写入,不能替代接线、供电和机械限位检查。

1.2.7 应用案例
1.2.7.1 舵机控制示例

视频为第二只舵机做了三处可见修改:声明 Servo myservo2、在 setup() 中执行 myservo2.attach(10)、在角度递增循环中执行 myservo2.write(pos)。随后把上方舵机的黄色信号线接 D10,电源和地接入同一组 5V/GND 扩展口,再重新上传程序。

最终引脚分工是:

D9  → 下方舵机信号 → 左右旋转(pan)
D10 → 上方舵机信号 → 上下俯仰(tilt)
5V  → 两只舵机电源(课程演示接法)
GND → 两只舵机地,同时也是控制信号参考地

必须指出,视频口述称第二只舵机也会“从 180°减到 0°”,并把演示描述为两轴同步;但逐帧核对最终编译、上传的草图后,可以看到反向循环里只有 myservo.write(pos),没有向 myservo2 写入递减的 pos。因此页首源码的真实逻辑是:

  • 递增阶段:两只舵机都依次收到 0~180 的目标值。
  • 递减阶段:只有 D9 对应的第一只舵机依次收到 180~0;第二只舵机保持最后写入的 180 目标。
  • 下一轮重新进入递增阶段时,第二只舵机的目标会突然从 180 改为 0,然后又从较小值继续递增;它不会按该源码平滑完成 180~0 的反向扫描。

所以这段最终代码不能证明“双轴完整往返同步”。精校稿依据 README 保留视频源码,不在代码块中擅自补齐缺失语句;若项目要求两轴在两个方向都跟随同一角度序列,两个方向的循环都必须分别向两个舵机对象更新目标。

即使两个对象都收到相同的 pos,两个转轴的机械安装方向也不同,相同数值不代表平台在空间中朝同一方向。真正的二维追光还需要光敏传感器输入、误差判断、两个轴各自的角度边界和校准逻辑;本节只演示开环扫描,没有读取任何光照值,因此视频结尾说的“追光效果”是后续用途,不是本节已经实现的自动追光。

1.3 上电验收与故障排查

现象优先检查
两只舵机都不动5V/GND 是否接反或断路、UNO 是否已运行、是否选择了正确端口和开发板
只有一只完全不动对应 D9/D10 信号线、该对象的 attach()、插头接触和舵机本体
第二只只正向扫或周期边界抽动页首最终代码只在递增循环更新 myservo2,这是源码逻辑而非必然的接线故障
舵机抖动、UNO 复位供电压降、外部电源未共地、插头松动、机械卡涩或负载过大
转向与课件相反舵机安装方向或舵盘齿位不同;先校准,不要仅凭左右/上下文字硬改接线
到端点持续嗡鸣或支架碰撞命令范围超过真实机械安全范围,应立即断电并缩小角度范围、重新校准舵盘

完成测试后,应先确认两轴没有碰撞、线缆没有被拉紧,再安装太阳能板。负载增加后,原来空载可用的供电和角度范围仍可能失效,不能把本节空载演示直接当成整机验收结果。

核对依据

  • 本地 videos/311.6、编程实现控制FPV舵机云台.mp4:逐段核对 20 ms/脉宽课件、Arduino IDE 2.0.1、Servo → Sweep 示例、D9 单舵机测试、D10 第二舵机接线、最终上传及实物演示;重点逐帧确认最终源码只在递增循环调用 myservo2.write(pos)。
  • 本地 transcripts/311.6、编程实现控制FPV舵机云台.txt:核对讲解顺序、线色、D9/D10 分工、COM3 和口述的双轴往返意图;对照视频纠正转录中的 “server”“sweet”“P-down”等识别错误,也没有用口述覆盖画面中实际缺失的反向控制语句。
  • Arduino Servo 库官方 Sweep 示例:核对示例名称、对象、D9、0~180/180~0 两个循环和 15 ms 步进。
  • Arduino Servo 库官方 API:核对 attach()、write()、默认 544/2400 µs 端点、角度含义及 D9/D10 普通 PWM 与 Servo 库的资源关系。
  • Arduino Servo 库 AVR 实现与定时器定义:核对 attach() 将所选数字脚设为输出、Timer1 中断依次产生各通道脉冲、20 ms 刷新和角度到脉宽的映射,从而纠正“必须使用带 ~ 的 PWM 脚”这一说法。
  • Arduino UNO Rev3 官方资料:核对 UNO 的 14 个数字 I/O,以及供 analogWrite() 使用的六个 PWM 引脚 D3、D5、D6、D9、D10、D11。
  • TowerPro SG90 Analog 官方规格:核对正品 SG90 Analog 的 4.8 V 工作电压和外部适配器供电建议;课程实物可能是兼容品,不能据此直接断言其所有电气参数。