深色模式
第 7 章:舵机 + 超声波避障
7.1 任务描述
本章实现自动避障:小车前进过程中持续测量前方距离;检测到障碍物时停车,由舵机带动超声波模块分别向左、向右测距,比较两侧距离后向空旷一侧转向,然后继续前进。全程无需人工干预。
7.2 避障策略
| 检测条件 | 小车动作 |
|---|---|
| 前方距离大于 30 cm | 前进(调用第 4 章"前进"函数) |
| 前方距离不大于 30 cm | 停车,舵机分别转向两侧测距 |
| 左侧距离大于右侧距离 | 左平移(或原地左转)后恢复前进 |
| 右侧距离不小于左侧距离 | 右平移(或原地右转)后恢复前进 |
| 两侧距离均不大于 15 cm | 后退 1 秒,再重新扫描 |

两个关键细节:
舵机转到目标角度后须等待约 0.5 秒再读取距离,否则读到的是转动途中的数据;
超声波模块存在约 2 cm 的测距盲区,且过近时回波异常,因此以 30 cm 作为减速判定线,为转向留出余地。
7.3 代码实现
程序结构:
循环开始,舵机回到 90° 中位,读取正前方距离;
若距离大于 30 cm,前进;
否则停车:舵机转到 135°,等待 0.5 秒,读取并记录左侧距离;舵机转到 45°,等待 0.5 秒,读取并记录右侧距离;
比较两侧距离,向空旷一侧平移或原地转向,回到第 1 步。
说明:每次循环先将舵机回到 90° 中位再测正前方距离,保证每次读数的探测方向一致,避免上一次扫描残留的角度影响判断。
本章无新知识点,Mind+ 图形化代码从略。重点在于通过算法锻炼编程思维。学员可根据下方 MicroPython 代码自行拖拽还原图形化程序,如需协助请联系编者微信。
python
# 自动避障主程序
# 直接调用前面章节的函数:servo_angle(第 5 章)、distance(第 5 章)、
# go_sp / stop_all(第 4 章),无需重复定义
while True:
# 每次循环先把舵机回 90° 中位再测正前方,
# 保证每次读数的探测方向一致,不受上次扫描残留角度影响
servo_angle(90)
time.sleep(0.5) # 等舵机停稳再读数
front = distance()
if front > 30:
# 前方空旷(>30cm):前进。以 30cm 为判定线,是因为超声波有约 2cm 盲区,
# 且要为停车、转向留出反应距离
go_sp(150, 1)
else:
stop_all() # 先停车,再左右扫描
servo_angle(135) # 135° = 左侧(俯视逆时针数角度)
time.sleep(0.5) # 等舵机停稳再读,否则读到的是转动途中的数据
left = distance()
servo_angle(45) # 45° = 右侧
time.sleep(0.5)
right = distance()
if left <= 15 and right <= 15:
# 两侧都很近(被围住了):后退 1 秒,下一轮重新扫描
go_sp(-150, 1)
time.sleep(1)
elif left > right:
# 左侧更空旷:向左平移 0.5 秒
# (也可以换成原地左转:turn_angle(30),效果类似)
go_sp(150, 2)
time.sleep(0.5)
else:
# 右侧不窄于左侧:向右平移 0.5 秒(或 turn_angle(-30))
go_sp(-150, 2)
time.sleep(0.5)
time.sleep(0.05)cpp
// 自动避障主程序
// 直接调用前面章节的函数:servoAngle(第 5 章)、distance(第 5 章)、
// go_sp / stop_all(第 4 章),无需重复定义
void loop() {
// 每次循环先把舵机回 90° 中位再测正前方,保证探测方向一致
servo15.write(90);
delay(500); // 等舵机停稳再读数
float front = distance();
if (front > 30) {
// 前方空旷(>30cm):前进。30cm 判定线为停车、转向留出反应距离
go_sp(150, 1);
} else {
stop_all(); // 先停车,再左右扫描
servo15.write(135); // 135° = 左侧(俯视逆时针数角度)
delay(500); // 等舵机停稳再读,否则读到转动途中的数据
float left = distance();
servo15.write(45); // 45° = 右侧
delay(500);
float right = distance();
if (left <= 15 && right <= 15) {
// 两侧都很近(被围住了):后退 1 秒,下一轮重新扫描
go_sp(-150, 1);
delay(1000);
} else if (left > right) {
// 左侧更空旷:向左平移 0.5 秒(也可换成原地左转 turn_angle(30))
go_sp(150, 2);
delay(500);
} else {
// 右侧不窄于左侧:向右平移 0.5 秒(或 turn_angle(-30))
go_sp(-150, 2);
delay(500);
}
}
delay(50);
}7.4 进阶练习:带扫描功能的跟随
当目标离开正前方探测范围时,小车会因读数异常而停止或误动作。改进方案:通过舵机(GP15)驱动超声波模块依次转向 45°、90°、135° 进行扫描,比较各方向的距离读数,判定目标方位后向该方向平移或原地转向,重新锁定目标。
本章无新知识点,Mind+ 图形化代码从略。重点在于通过算法锻炼编程思维。学员可根据下方 MicroPython 代码自行拖拽还原图形化程序,如需协助请联系编者微信。
python
# 进阶:带扫描功能的跟随
# 思路:舵机带超声波扫 45°/90°/135° 三个方向,
# 哪个方向距离最大,目标就在哪个方向,然后向该方向移动
while True:
best_dir = 90 # 记录距离最大的方向
best_dist = -1 # 记录最大距离(先设成不可能的小值)
for a in (45, 90, 135):
servo_angle(a)
time.sleep(0.5) # 等舵机停稳
s = distance()
if s > best_dist:
best_dist = s
best_dir = a
# 扫描结束,按目标方位行动
if best_dir == 90:
# 目标在正前方:按磁吸跟随的死区逻辑控制距离(第 6 章)
if best_dist > 30:
go_sp(150, 1) # 远了:前进
elif best_dist < 15:
go_sp(-150, 1) # 太近:后退
else:
stop_all() # 15~30cm:刚好,停车
elif best_dir == 135:
# 目标在左侧:向左平移 0.5 秒重新锁定(也可 turn_angle(30))
go_sp(150, 2)
time.sleep(0.5)
else:
# 目标在右侧:向右平移 0.5 秒(也可 turn_angle(-30))
go_sp(-150, 2)
time.sleep(0.5)
time.sleep(0.05)cpp
// 进阶:带扫描功能的跟随
// 思路:扫 45°/90°/135° 三个方向,距离最大的方向就是目标方向
void loop() {
int best_dir = 90; // 记录距离最大的方向
float best_dist = -1; // 记录最大距离(先设成不可能的小值)
int angles[3] = {45, 90, 135};
for (int i = 0; i < 3; i++) {
servo15.write(angles[i]);
delay(500); // 等舵机停稳
float s = distance();
if (s > best_dist) {
best_dist = s;
best_dir = angles[i];
}
}
// 扫描结束,按目标方位行动
if (best_dir == 90) {
// 目标在正前方:按磁吸跟随的死区逻辑控制距离(第 6 章)
if (best_dist > 30) {
go_sp(150, 1); // 远了:前进
} else if (best_dist < 15) {
go_sp(-150, 1); // 太近:后退
} else {
stop_all(); // 15~30cm:刚好,停车
}
} else if (best_dir == 135) {
// 目标在左侧:向左平移 0.5 秒重新锁定(也可 turn_angle(30))
go_sp(150, 2);
delay(500);
} else {
// 目标在右侧:向右平移 0.5 秒(也可 turn_angle(-30))
go_sp(-150, 2);
delay(500);
}
delay(50);
}7.5 拓展练习
脱困能力:模拟三面被围的场景,验证"两侧均过近则后退重扫"分支能否让小车脱困;
效率统计:用变量记录一次运行中的转向次数,比较不同判定阈值(20 cm / 30 cm / 40 cm)下的通行效率。
