深色模式
第 5 章:控制舵机转动(SG90)
5.1 模块原理:舵机与 PWM 角度控制
舵机(伺服电机)与普通直流电机的区别在于:它不连续旋转,而是根据控制信号转动到指定角度并保持。本车使用的 SG90 舵机控制原理为:信号端接收周期 20ms(频率 50Hz)的 PWM 信号,脉冲宽度在 0.5ms~2.5ms 之间变化时,输出轴对应转到 0°~180° 范围内的相应角度,脉冲宽度与角度近似线性对应(如 1.5ms 对应 90° 中位)。本车 SG90 从俯视角度看为逆时针旋转 180°。
SG90 有三根引线:棕色为 GND,红色为 VCC(5V),橙色为信号线。D3 上的接线为:信号线接 GP15,供电接拓展板 USB(5V)供电输出区(红 + 棕 −,10 公母)。舵机上方安装超声波模块(⑦),改变舵机角度即可改变探测方向。
在 Mind+ 中,舵机控制积木已封装脉宽换算,直接设置目标角度即可。使用前需先在"扩展"中加载舵机(执行器)模块。
SG90 舵机支持 0–180° 逆时针转动。实际编程建议设定 10–170°,预留约 10° 余量,防止过转卡死。


5.2 接线检查
接线出厂时已完成,编程前确认舵机插头无松动即可。舵机供电为 5V,信号电平由 Pico 输出,两者已在拓展板上完成匹配。
5.3 代码实现:三个角度定位
目标:舵机依次转到 0° → 90° → 180° → 90°,每个角度停留 1 秒,循环执行。观察舵机转向与角度值的对应关系。
运行以下代码,如果发现舵机设置为90°时,超声波模块两只小眼睛没有真对正前方,请转跳至6.3舵机回正90°解决方法查看操作方法。

python
from machine import Pin, PWM
import time
# SG90 舵机:50Hz 固定频率,角度靠"脉宽"控制
servo = PWM(Pin(15), freq=50)
def servo_angle(a):
# 舵机 0~180° 对应高电平脉宽 0.5ms ~ 2.5ms(周期 20ms)
# 换算成 duty_u16:0.5/20 × 65535 ≈ 1638,2.5/20 × 65535 ≈ 8191
# 1638 + (a/180) × 6553 就把角度线性映射到脉宽
servo.duty_u16(int(1638 + (a / 180) * 6553))
while True:
servo_angle(10) # 打 10° 而不打 0°:留余量,防止舵机顶到机械极限卡死
time.sleep(1)
servo_angle(90) # 正前方
time.sleep(1)
servo_angle(170) # 同理,170° 代替 180°
time.sleep(1)
servo_angle(90)
time.sleep(1)cpp
#include <Servo.h>
Servo servo15; // GP15 上的 SG90 舵机
void setup() {
servo15.attach(15); // Servo 库自动生成 50Hz 舵机信号
}
void loop() {
// write(角度) 直接给角度,库内部完成脉宽换算
servo15.write(10); // 打 10° 而不打 0°:留余量,防止舵机顶到机械极限卡死
delay(1000);
servo15.write(90); // 正前方
delay(1000);
servo15.write(170); // 同理,170° 代替 180°
delay(1000);
servo15.write(90);
delay(1000);
}注意:舵机从当前角度转到目标角度需要时间(SG90 约 0.1 秒/60°)。程序中每次设置角度后必须保留足够的等待时间(大角度转动至少 0.5~1 秒),否则会出现"指令已执行、舵机未到位"的错位,后续动作将全部跑偏。
5.4 代码实现:带动超声波改变探测方向
目标:舵机带动超声波模块依次停在 45°、90°、135° 三个方向,各停留 1 秒。该动作是第 7 章(避障与扫描跟随)中"扫描"功能的基础。

python
from machine import Pin, time_pulse_us
import time
# 超声波:Trig 触发脚 GP21(输出),Echo 回响脚 GP20(输入)
trig = Pin(21, Pin.OUT)
echo = Pin(20, Pin.IN)
def distance():
# 原理:发一束超声波,等它撞到障碍物反射回来
# 距离 = 声速 × 往返时间 ÷ 2(÷2 因为声波走了来回两趟)
trig.value(0)
time.sleep_us(2)
trig.value(1) # 拉高 10 微秒 = 发出触发信号
time.sleep_us(10)
trig.value(0)
# time_pulse_us 测量 Echo 脚高电平持续的微秒数,超时 30ms 返回 -1
# 0.0343 = 声速 343m/s 换算成 cm/μs
return time_pulse_us(echo, 1, 30000) * 0.0343 / 2
def scan3():
# 对应 Mind+ 自定义积木「舵机往复运动带动超声波改变探测方向」
# 俯视逆时针数角度:45° = 右侧,90° = 正前方,135° = 左侧
# 每到一个位置先等 1 秒让舵机停稳再测,否则读到的是转动途中的数据
servo_angle(45)
time.sleep(1)
print("右侧 45° 距离:", distance(), "厘米")
servo_angle(90)
time.sleep(1)
print("前方 90° 距离:", distance(), "厘米")
servo_angle(135)
time.sleep(1)
print("左侧 135° 距离:", distance(), "厘米")
# 回程:135° → 90°,顺便复测一次正前方
# (截图第 4 个舵机积木是 135° 的笔误,应为 90°,代码按 90° 写)
servo_angle(90)
time.sleep(1)
print("前方 90° 距离:", distance(), "厘米")
# 主程序里循环调用 scan3() 即可,例如:
# while True:
# scan3()cpp
// 超声波:Trig 触发脚 GP21(输出),Echo 回响脚 GP20(输入)
const int TRIG = 21;
const int ECHO = 20;
float distance() {
// 原理:发一束超声波,等它撞到障碍物反射回来
// 距离 = 声速 × 往返时间 ÷ 2(÷2 因为声波走了来回两趟)
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH); // 拉高 10 微秒 = 发出触发信号
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
// pulseIn 测量 Echo 脚高电平持续的微秒数,超时 30000μs 返回 0
// 0.0343 = 声速 343m/s 换算成 cm/μs
return pulseIn(ECHO, HIGH, 30000) * 0.0343 / 2;
}
void scan3() {
// 对应 Mind+ 自定义积木「舵机往复运动带动超声波改变探测方向」
// 俯视逆时针数角度:45° = 右侧,90° = 正前方,135° = 左侧
// 每到一个位置先等 1 秒让舵机停稳再测
servo15.write(45);
delay(1000);
Serial.print("右侧 45° 距离:"); Serial.print(distance()); Serial.println(" 厘米");
servo15.write(90);
delay(1000);
Serial.print("前方 90° 距离:"); Serial.print(distance()); Serial.println(" 厘米");
servo15.write(135);
delay(1000);
Serial.print("左侧 135° 距离:"); Serial.print(distance()); Serial.println(" 厘米");
// 回程:135° → 90°,顺便复测一次正前方
// (截图第 4 个舵机积木是 135° 的笔误,应为 90°,代码按 90° 写)
servo15.write(90);
delay(1000);
Serial.print("前方 90° 距离:"); Serial.print(distance()); Serial.println(" 厘米");
}
// 主程序里循环调用 scan3() 即可,例如:
// void loop() {
// scan3();
// }不推崇暴力堆叠的屎山代码。上述逻辑可用循环优化:每0.1秒旋转1°,实现精度更高的180°旋转。
5.5 代码实现:舵机带动超声波实现雷达效果

python
def radar():
# 对应 Mind+ 自定义积木「舵机往复运动带动超声波改变探测方向实现雷达效果」
k = 1 # 步长方向:+1 角度越来越大,-1 角度越来越小
starting_Angle = 10 # 当前角度,从 10° 出发
servo_angle(starting_Angle)
time.sleep(2) # 先停 2 秒,等舵机转到起始位再开始扫描
while True:
# 关键一步:角度每轮走一步(k 是 +1 就往前,是 -1 就往回)
starting_Angle = starting_Angle + k
servo_angle(starting_Angle)
time.sleep(0.1) # 每步停 0.1 秒,扫描更平滑
if starting_Angle >= 170:
k = -1 # 扫到 170° 掉头:步长变负,角度开始往回走
if starting_Angle <= 10:
k = 1 # 回到 10° 再掉头:步长变正,重新向前
# 两个"如果"配合,舵机就在 10° ↔ 170° 之间往返摆头,这就是雷达效果cpp
// 对应 Mind+ 自定义积木「舵机往复运动带动超声波改变探测方向实现雷达效果」
int k = 1; // 步长方向:+1 角度越来越大,-1 角度越来越小
int starting_Angle = 10; // 当前角度,从 10° 出发
void setup() {
servo15.attach(15);
servo15.write(starting_Angle);
delay(2000); // 先停 2 秒,等舵机转到起始位再开始扫描
}
void loop() {
// 关键一步:角度每轮走一步(k 是 +1 就往前,是 -1 就往回)
starting_Angle = starting_Angle + k;
servo15.write(starting_Angle);
delay(100); // 每步停 0.1 秒,扫描更平滑
if (starting_Angle >= 170) {
k = -1; // 扫到 170° 掉头:步长变负,角度开始往回走
}
if (starting_Angle <= 10) {
k = 1; // 回到 10° 再掉头:步长变正,重新向前
}
// 两个"如果"配合,舵机就在 10° ↔ 170° 之间往返摆头,这就是雷达效果
}重要的事情说三遍:SG90舵机沿逆时针方向可以实现0-180角度转动,实际编程中,应当留有余量10左右余量,过转会卡死,推荐程序设定10-170°,留有余量。
