搜索代码

课程 第9课,共10课:使用树莓派Pico 4WD智能车进行避障

本课是……的一部分: Raspberry Pi Pico 四驱智能车

课程 第9课,共10课:使用树莓派Pico 4WD智能车进行避障

简介

本指南基于Robojax课程第9课(共10课),演示如何使用SunFounder树莓派Pico四驱智能车套件构建避障机器人。该项目将舵机驱动的超声波传感器与电机控制相结合,打造出一款能够自主导航环境的机器人。通过像雷达一样扫描传感器,小车能够检测障碍物并智能选择畅通路径前进,是学习机器人技术、传感器集成和自主导航的绝佳入门项目。

Sunfounder_Pico4DW-09-避障设置,地面有障碍物

这个项目不仅仅是玩具,它是许多实际应用的基础。以下是一些启发您的想法:

  • 自主探索机器人:构建一款无需人工干预即可绘制并探索房间或室外区域的机器人。
  • 智能吸尘器:利用相同原理创建一款在房间内导航、避开家具和墙壁的同时进行清洁的机器人。
  • 安防巡逻机器人:编程小车沿周界巡逻,并使用超声波传感器检测和报告意外障碍物或入侵。
  • 人工智能教育平台:将此项目作为学习更复杂算法(如路径规划和同步定位与地图构建(SLAM))的跳板。

本课重点介绍使小车做出决策的核心逻辑和代码。小车使用舵机旋转超声波传感器,像雷达一样测量0到180度的距离。代码分析这些数据以找到最宽的空旷路径,并引导小车相应地前进、左转或右转。视频演示了整个过程,从测试各个组件到在真实迷宫环境中运行最终的避障算法。

硬件/组件

本项目基于SunFounder树莓派Pico四驱智能车套件。本课使用的基本组件包括:

  • 树莓派Pico微控制器板
  • SunFounder四驱智能车底盘,带四个电机
  • 电机驱动板
  • 超声波距离传感器(HC-SR04类型)
  • 舵机(用于旋转超声波传感器)
  • 电源(小车电池)

接线指南

SunFounder套件设计为易于组装,接线通常通过专用扩展板预先连接。超声波传感器安装在舵机上,然后连接到主控制板。这种设置允许传感器旋转以扫描环境。有关适用于您套件的详细接线图,请参阅SunFounder官方文档。

核心连接涉及超声波传感器的触发和回波引脚,以及舵机的信号引脚,所有这些都连接到树莓派Pico上的特定GPIO引脚。电机驱动器也连接到Pico以控制四个驱动电机。视频中提到,包括接线图在内的完整文档可随套件下载。

Sunfounder_Pico4DW-09-避障_超声波检测到障碍物时

代码说明

本课重点介绍`obstacle_avoid`示例中的逻辑。代码使用MicroPython编写。虽然完整程序可供下载,但我们将重点介绍控制小车行为的关键、用户可配置参数。这些参数通常位于脚本顶部。

以下是可以调整以优化机器人性能的主要变量:


# 雷达和运动参数
radar_reference = 20  # 被视为安全的最小距离(厘米)
step_angle = 10       # 每次雷达扫描步进的角度增量
forward_speed = 30    # 前进的电机速度(0-100)
turn_speed = 50       # 转弯的电机速度(0-100)
forward_scan_angle = 50  # 未检测到障碍物时舵机扫描的角度
  • radar_reference:这是关键的安全距离(厘米)。如果检测到障碍物距离小于此值,小车将视为“危险”并启动避障操作。在视频中,此值设置为20厘米。
  • step_angle:这定义了雷达扫描的分辨率。较小的值(例如10度)意味着更详细的扫描,但耗时更长。较大的值会更快,但可能漏掉狭窄的障碍物。
  • forward_speed和turn_speed:这些控制发送到电机的功率。您可以调整它们以改变小车的整体速度和灵活性。转弯速度通常设置得更高,以确保小车能够有效旋转。
  • forward_scan_angle:当正前方没有立即危险时,小车将扫描范围缩小到此角度(以正前方为中心),以更频繁地检查其直接路径上的障碍物,从而实现更平滑的前进运动。

视频还解释了主要的决策逻辑。函数`get_direction()`分析距离读数列表(转换为1和0表示安全/不安全),以找到最宽的连续“1”路径。根据这条最宽路径的位置,函数返回一个方向:“前进”、“左转”或“右转”。主循环随后执行相应的操作,转动小车并重新扫描,直到前方路径畅通。

实际项目/演示

视频对该项目进行了全面的演示。首先单独测试超声波传感器,展示其如何测量距离并将数值打印到终端。随后,讲师使用流程图解释避障算法,详细说明左转和右转的决策过程。最后,将小车放入由箱子搭建的小型迷宫中,展示完整的避障行为。小车成功穿越迷宫,通过左转和右转找到前方畅通的路径。

视频章节

  • [00:00] 避障功能简介
  • [01:31] SunFounder智能小车套件及其功能概述
  • [02:47] 避障功能的工作原理
  • [03:34] 测试超声波传感器
  • [07:45] 解释避障算法和流程图
  • [12:49] 分析主要避障代码
  • [15:43] 避障逻辑的现场演示
  • [22:53] 使用障碍物迷宫的最终演示
  • [24:35] 总结与后续步骤

图像

pico-4wd_main
pico-4wd_main
PICOBOARDWHITEANGLE2_1200x857
PICOBOARDWHITEANGLE2_1200x857
Sunfounder_Pico4DW-09-Obstacle-avoidance setup with obstacle on the floor
Sunfounder_Pico4DW-09-Obstacle-avoidance setup with obstacle on the floor
Sunfounder_Pico4DW-09-Obstacle-avoidance_when ultrasonic detects obstacle
Sunfounder_Pico4DW-09-Obstacle-avoidance_when ultrasonic detects obstacle
909-Lesson 9/10 Pi Pico 4WD card Python code: obstacle avoid
语言: C++
# Lesson 9/10 Pi Car Pico 4WD: Obstacle avoid
# Download and resource page https://robojax.com/RJT631
import pico_4wd as car
import time

car.RADAR_REFERENCE = 20
car.RADAR_STEP_ANGLE = 10
MOTOR_FORWARD_POWER = 30
MOTOR_TURNING_POWER = 50

FORWARD_SCAN_ANGLE = 50

car.set_radar_scan_angle(FORWARD_SCAN_ANGLE)
status = "Danger"

def get_dir(radar_data):
    # get scan status of 0, 1
    # print(radar_data)
    radar_data = [str(i) for i in radar_data]
    radar_data = "".join(radar_data)
    # Split 0, leaves the free path
    paths = radar_data.split("0")
    # print(paths)
    # print("paths: %s" % paths)
    length_list = []
    # Calculate where is the widest
    for path in paths:
        length_list.append(len(path))
    if max(length_list) < 4:
        return "left"
    # Calculate the direction of the widest 
    i = length_list.index(max(length_list))
    pos = radar_data.index(paths[i])
    pos += (len(paths[i]) - 1) / 2
    delta = len(radar_data) / 3
    
    if pos < delta:
        return "left"
    elif pos > 2 * delta:
        return "right"
    else:
        return "forward"

def main():
    global status
    # General
    while True:
        radar_data = car.radar_scan()
        # print("radar_data: %s" % radar_data)
        # If radar data return a int, means scan not finished, and the int is current angle status
        if isinstance(radar_data, int):
            if radar_data == 0 and status != "Danger":
                print("Danger!")
                status = "Danger"
                car.move("stop")
                car.set_radar_scan_angle(180)
            continue
        else:
            status = "Save"
        
        direction = get_dir(radar_data)

        if direction == "left":
            print("turn left")
            distance = car.get_radar_distance_at(-FORWARD_SCAN_ANGLE/2)
            time.sleep(0.5)
            car.move("left", MOTOR_TURNING_POWER)
            while True:
                distance = car.get_radar_distance_at(-FORWARD_SCAN_ANGLE/2)
                status = car.get_radar_status(distance)
                if status == 1:
                    break
            car.move("stop")
            car.set_radar_scan_angle(FORWARD_SCAN_ANGLE)
        elif direction == "right":
            print("turn right")
            distance = car.get_radar_distance_at(FORWARD_SCAN_ANGLE/2)
            time.sleep(0.5)
            car.move("right", MOTOR_TURNING_POWER)
            while True:
                distance = car.get_radar_distance_at(FORWARD_SCAN_ANGLE/2)
                status = car.get_radar_status(distance)
                if status == 1:
                    break
            car.move("stop")
            car.set_radar_scan_angle(FORWARD_SCAN_ANGLE)
        else:
            print("forward")
            car.set_radar_scan_angle(FORWARD_SCAN_ANGLE)
            car.move("forward", MOTOR_FORWARD_POWER)

try:
    main()
finally:
    car.move("stop")
    car.set_light_off()

|||您可能需要的东西

资源与参考

尚无可用资源。

文件📁

树莓派代码