网页审阅副本 · 源文件:07-智能机器人系统.md
本章目录

第七章 智能机器人系统

7.1 引言:智能机器人系统概览

想象一个能够用步态和动作表达性格的双足机器人角色。2024年,迪士尼研究院公开了相关原型及其设计与控制方法,为研究“机械设计、运动控制与角色动画如何协同”提供了一个案例。

开篇案例:迪士尼研究院的双足机器人

该项目重点展示了双足机构、强化学习控制、动画动作和操作员交互界面的结合。它说明机器人角色的效果来自机械、控制与内容设计的协同,并不意味着原型已经具备通用的自然语言理解或完全自主服务能力。这个案例也引出了本章的核心主题:机器人如何把感知数据转化为受约束、可验证的行动。

资料来源:Disney Research, “Design and Control of a Bipedal Robotic Character”。

智能机器人系统的核心组件

智能机器人系统,无论是迪士尼的双足机器人还是我们日常生活中的扫地机器人,都由以下核心组件构成:

  1. 感知系统:机器人的"眼睛"和"耳朵" * 传感器(如摄像头、麦克风、距离传感器等) * 数据处理和特征提取
  2. 决策系统:机器人的"大脑" * 环境理解和建模 * 任务规划 * 行为决策
  3. 控制系统:机器人的"肌肉" * 运动规划 * 执行器控制(如电机控制)
  4. 学习系统:机器人的"经验积累" * 数据收集和分析 * 模型更新和优化 * 适应性调整

这些组件紧密协作,使机器人能够感知环境、做出决策、执行动作,并从经验中学习和改进。

本章项目目标:验证从感知到决策的基本链路

在本章中,我们将通过硬件单项测试和网格仿真,理解智能机器人从感知、规划到决策的基本链路。课程不把尚未标定和验收的模块直接组合成“功能完整”的自主机器人。

我们的目标是验证系统是否能够:

的小型移动机器人。

学习目标

通过学习本章内容,你将能够:

知识目标

技能目标

无论你是对机器人充满好奇的初学者,还是希望深入了解智能系统的学生,本章都将为你提供一次激动人心的学习之旅。让我们一起踏上这个从数据到行动的智能机器人探索之路吧!

在下一节中,我们将开始我们的第一个实践项目:搭建机器人的硬件平台。准备好你的工具和热情,我们即将动手创造属于自己的智能机器人!

7.2 搭建机器人硬件平台

欢迎来到我们动手实践的第一步!在这一节中,我们将一起搭建一个简单但功能强大的机器人硬件平台。别担心,即使你从未接触过电路或编程,只要跟着我们的指导,你也能成功组装出自己的小机器人。

机器人硬件基础知识

在开始动手之前,让我们先了解一下构成机器人的基本硬件组件:

  1. 控制器:机器人的"大脑",我们将使用Raspberry Pi 4B。
  2. 机器人底盘:机器人的"身体",提供结构支撑和移动能力。
  3. 电机和轮子:让机器人能够移动。
  4. 电机驱动板:控制电机的转速和方向。
  5. 电池:为整个系统提供电力。
  6. 传感器:机器人的"感官",我们将在后续章节添加。

实践项目:组装基础机器人平台

硬件清单和采购建议

以下是我们需要的核心组件:

  1. Raspberry Pi 4B(4GB RAM)
  2. 两轮差速驱动机器人底盘套件
  3. L298N 电机驱动模块
  4. 与电机额定电压匹配的电池组,以及稳定的5V降压模块(为Raspberry Pi供电)
  5. 杜邦线若干
  6. 螺丝刀套装

采购建议:

详细组装步骤指南

  1. 准备底盘 * 按照底盘套件说明书组装底盘框架。 * 确保两个驱动轮安装牢固,能够自由转动。
  2. 安装电机 * 将两个直流电机固定到底盘指定位置。 * 检查电机轴与轮子是否对齐。
  3. 安装Raspberry Pi * 在底盘上找一个合适的位置安装Raspberry Pi。 * 使用螺丝或双面胶固定,确保稳固。
  4. 连接电机驱动板 * 将L298N电机驱动模块安装在Raspberry Pi旁边。 * 确保有足够的空间连接线缆。
  5. 安装电池 * 将电池安装在底盘的合适位置,保持重心平衡。 * 确保电池的输出端口易于接触。

基础电路连接

现在,让我们将各个部分连接起来:

  1. 电机连接 * 将左右电机分别连接到L298N的输出端A和B。
  2. L298N与Raspberry Pi连接 * 将L298N的IN1、IN2、IN3、IN4引脚分别连接到Raspberry Pi的GPIO引脚(例如GPIO 17, 18, 22, 23)。 * 将L298N的GND连接到Raspberry Pi的GND。
  3. 电源连接 * 按电机和驱动板的额定电压连接电池,不能仅因驱动板端子标有“12V”就使用12V电池。 * 通过额定电流足够的5V降压模块为Raspberry Pi供电;电机电源与控制器必须共地。 * 电池输出端应串联保险丝和总电源开关;调试运动程序时应准备可立即触发的急停方式。
  4. 检查连接 * 仔细检查所有连接,确保没有短路或接错。

安全注意事项

  1. 在连接电路时,始终断开电源。
  2. 注意电池的正负极,接反可能会损坏组件。
  3. 避免金属物品接触电路板,防止短路。
  4. 如果闻到异味或看到冒烟,立即断开电源并检查。
  5. 保持工作区域干净整洁,避免小零件丢失。

测试你的机器人平台

完成组装后,让我们进行一个简单的测试:

  1. 给系统通电。
  2. 观察Raspberry Pi的指示灯,确认其正常启动。
  3. 如果一切正常,恭喜你!你已经成功搭建了机器人的基础平台。

在下一节中,我们将学习如何为这个平台添加基本的传感器,使我们的机器人能够"感知"周围的世界。

记住,Rome wasn't built in a day(罗马不是一天建成的)。如果在组装过程中遇到困难,不要灰心。仔细检查每一步,如果需要,可以寻求同学或老师的帮助。每一个成功的工程师都是从这样的小项目开始的!

7.3 机器人的感知系统

想象一下,如果你突然失去了所有的感官,你将如何与世界互动?这正是没有传感器的机器人所面临的困境。在这一节中,我们将为我们的机器人添加"感官",使它能够感知周围的世界。

传感器类型和原理介绍

传感器是机器人的"感官",它们将物理世界的信息转换为电信号,供机器人的"大脑"(处理器)分析。以下是我们将使用的几种基本传感器:

  1. 摄像头:机器人的"眼睛" * 原理:捕捉光线并将其转换为数字图像 * 应用:物体识别、颜色检测、动作追踪
  2. 超声波传感器:机器人的"回声定位"能力 * 原理:发射高频声波并测量回波时间来计算距离 * 应用:障碍物检测、距离测量
  3. IMU (惯性测量单元):机器人的"平衡感" * 原理:测量加速度和角速度 * 应用:姿态估计、运动检测

实践项目:安装和测试基本传感器

硬件清单

  1. Raspberry Pi Camera Module V2
  2. HC-SR04 超声波传感器
  3. MPU-6050 IMU模块
  4. 面包板和杜邦线

硬件连接指南

  1. 安装摄像头 * 将摄像头模块连接到Raspberry Pi的摄像头接口 * 注意:小心操作柔性排线,避免损坏
  2. 连接超声波传感器 * VCC连接到Raspberry Pi的5V * GND连接到Raspberry Pi的GND * TRIG连接到GPIO25(本章统一使用BCM编号,避免与右电机GPIO23冲突) * ECHO输出为5V,不可直接连接Raspberry Pi的3.3V GPIO。使用电阻分压(例如ECHO—1 kΩ—GPIO24—2 kΩ—GND)或双向电平转换模块后再接GPIO24
  3. 连接IMU模块 * VCC连接到3.3V * GND连接到GND * SDA连接到GPIO2 * SCL连接到GPIO3

软件环境配置

  1. 更新Raspberry Pi系统:

    sudo apt update sudo apt upgrade 2. 安装必要的库:

    sudo apt install python3-picamera2 sudo apt install python3-pip pip3 install RPi.GPIO pip3 install mpu6050-raspberrypi 3. 启用I2C接口(用于IMU):

    sudo raspi-config

    在Interface Options中启用I2C

传感器数据读取和可视化

让我们编写一个简单的Python程序来读取和显示传感器数据:

import RPi.GPIO as GPIO
import time
from picamera2 import Picamera2
from mpu6050 import mpu6050

# 设置GPIO模式
GPIO.setmode(GPIO.BCM)

# 超声波传感器设置
TRIG = 25
ECHO = 24
GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)

# 初始化摄像头
camera = Picamera2()
camera.configure(camera.create_still_configuration())
camera.start()

# 初始化IMU
imu = mpu6050(0x68)

def get_distance(timeout=0.03):
    """返回厘米数;未在超时时间内收到回波时返回None。"""
    GPIO.output(TRIG, False)
    time.sleep(0.000002)
    GPIO.output(TRIG, True)
    time.sleep(0.00001)
    GPIO.output(TRIG, False)

    deadline = time.monotonic() + timeout
    while GPIO.input(ECHO) == 0:
        if time.monotonic() >= deadline:
            return None
    start_time = time.monotonic()

    deadline = time.monotonic() + timeout
    while GPIO.input(ECHO) == 1:
        if time.monotonic() >= deadline:
            return None
    stop_time = time.monotonic()

    time_elapsed = stop_time - start_time
    distance = (time_elapsed * 34300) / 2

    return distance

try:
    while True:
        # 读取超声波传感器数据
        dist = get_distance()
        if dist is None:
            print("Distance: timeout")
        else:
            print(f"Distance: {dist:.2f} cm")

        # 读取IMU数据
        accel_data = imu.get_accel_data()
        gyro_data = imu.get_gyro_data()
        print(f"Accelerometer data: {accel_data}")
        print(f"Gyroscope data: {gyro_data}")

        # 捕获图像
        camera.capture_file('image.jpg')
        print("Image captured")

        time.sleep(1)

except KeyboardInterrupt:
    print("Measurement stopped by user")
finally:
    GPIO.cleanup()
    camera.stop()

运行这个程序,你将看到来自各个传感器的数据流。恭喜你,你的机器人现在已经能"看"、"听"和"感觉"了!

挑战与思考

  1. 尝试调整超声波传感器的位置,看看它如何影响距离测量。
  2. 用IMU数据检测机器人是否在移动或倾斜。
  3. 利用摄像头拍摄的图像,想想我们如何让机器人识别特定物体?

在下一节中,我们将学习如何利用这些感知数据来控制机器人的运动。记住,优秀的工程师不仅要会使用工具,还要理解工具的工作原理。继续探索,继续提问!

多传感器数据的边界

不同传感器只有在描述同一状态量、单位和坐标系已经统一,并且时间戳对齐后,才能进行有意义的融合。HC-SR04测得的是传感器到前方反射面的距离,IMU测得的是机体加速度和角速度,二者不能直接加权平均为“位移”。本章只分别记录这些数据,并把严格的标定、姿态估计和卡尔曼滤波作为拓展内容。

思考:如果要估计机器人的位姿,还需要哪些信息(例如轮编码器、传感器外参和时间戳)?

7.4 基础运动控制

现在我们的机器人已经能够采集感知数据了。本节学习在受控条件下完成前进、后退和转向测试。首次运行时应架空驱动轮,并由一人随时断开总电源。

电机控制原理

在开始编程之前,让我们先了解一下电机控制的基本原理:

  1. 直流电机:我们的机器人使用的是直流电机,通过改变施加在电机上的电压,我们可以控制电机的转速和方向。
  2. H桥:这是控制直流电机的关键电路。想象一个字母"H",电机连接在横杠上,通过控制四个开关(实际上是晶体管),我们可以改变电流方向,从而改变电机旋转方向。
  3. PWM(脉冲宽度调制):通过快速开关电源(每秒数千次),我们可以控制施加到电机的平均电压,从而控制速度。占空比越高,电机转速越快。

实践项目:实现简单的遥控功能

步骤1:基本电机控制

首先,让我们编写一个基本的Python脚本来控制电机:

import RPi.GPIO as GPIO
import time

# 设置GPIO模式
GPIO.setmode(GPIO.BCM)

# 定义电机控制引脚
LEFT_FORWARD = 17
LEFT_BACKWARD = 18
RIGHT_FORWARD = 22
RIGHT_BACKWARD = 23

# 设置引脚为输出
GPIO.setup(LEFT_FORWARD, GPIO.OUT)
GPIO.setup(LEFT_BACKWARD, GPIO.OUT)
GPIO.setup(RIGHT_FORWARD, GPIO.OUT)
GPIO.setup(RIGHT_BACKWARD, GPIO.OUT)

# 创建PWM对象
LEFT_PWM_F = GPIO.PWM(LEFT_FORWARD, 100)
LEFT_PWM_B = GPIO.PWM(LEFT_BACKWARD, 100)
RIGHT_PWM_F = GPIO.PWM(RIGHT_FORWARD, 100)
RIGHT_PWM_B = GPIO.PWM(RIGHT_BACKWARD, 100)

# 启动PWM
LEFT_PWM_F.start(0)
LEFT_PWM_B.start(0)
RIGHT_PWM_F.start(0)
RIGHT_PWM_B.start(0)

def set_motor_speed(left_speed, right_speed):
    left_speed = max(-100, min(100, left_speed))
    right_speed = max(-100, min(100, right_speed))
    if left_speed >= 0:
        LEFT_PWM_F.ChangeDutyCycle(left_speed)
        LEFT_PWM_B.ChangeDutyCycle(0)
    else:
        LEFT_PWM_F.ChangeDutyCycle(0)
        LEFT_PWM_B.ChangeDutyCycle(-left_speed)

    if right_speed >= 0:
        RIGHT_PWM_F.ChangeDutyCycle(right_speed)
        RIGHT_PWM_B.ChangeDutyCycle(0)
    else:
        RIGHT_PWM_F.ChangeDutyCycle(0)
        RIGHT_PWM_B.ChangeDutyCycle(-right_speed)

# 测试电机控制
try:
    print("前进")
    set_motor_speed(30, 30)
    time.sleep(0.5)
    set_motor_speed(0, 0)
    time.sleep(0.3)

    print("后退")
    set_motor_speed(-30, -30)
    time.sleep(0.5)
    set_motor_speed(0, 0)
    time.sleep(0.3)

    print("左转")
    set_motor_speed(-25, 25)
    time.sleep(0.4)
    set_motor_speed(0, 0)
    time.sleep(0.3)

    print("右转")
    set_motor_speed(25, -25)
    time.sleep(0.4)

    print("停止")
    set_motor_speed(0, 0)

except KeyboardInterrupt:
    print("程序被用户中断")

finally:
    set_motor_speed(0, 0)
    GPIO.cleanup()

步骤2:键盘控制

现在,让我们添加键盘控制。将下面的循环替换步骤1中的“测试电机控制”部分,并保留前面的GPIO和PWM初始化代码;不要在已经执行GPIO.cleanup()后单独运行此片段。

import curses

# 初始化curses
screen = curses.initscr()
curses.noecho()
curses.cbreak()
screen.keypad(True)
screen.timeout(200)  # 200毫秒未收到按键就停车

try:
    while True:
        char = screen.getch()
        if char == -1:
            set_motor_speed(0, 0)
        elif char == ord('q'):
            break
        elif char == curses.KEY_UP:
            print("前进")
            set_motor_speed(50, 50)
        elif char == curses.KEY_DOWN:
            print("后退")
            set_motor_speed(-50, -50)
        elif char == curses.KEY_LEFT:
            print("左转")
            set_motor_speed(-30, 30)
        elif char == curses.KEY_RIGHT:
            print("右转")
            set_motor_speed(30, -30)
        elif char == ord(' '):
            print("停止")
            set_motor_speed(0, 0)

finally:
    set_motor_speed(0, 0)
    # 恢复终端设置
    curses.nocbreak()
    screen.keypad(False)
    curses.echo()
    curses.endwin()

    # 清理GPIO设置
    GPIO.cleanup()

安全注意事项

  1. 始终确保电机连接正确,错误的连接可能导致短路。
  2. 在进行任何硬件更改时,请断开电源。
  3. 在测试时,将机器人放在一个安全的位置,防止它意外跑出。
  4. 如果电机或电路变得异常热,立即关闭电源并检查。

挑战与思考

  1. 尝试实现速度控制:如何让机器人缓慢加速或减速?
  2. 思考如何实现更平滑的转弯。
  3. 挑战:能否让机器人按照预设的路径移动,例如画一个正方形?
  4. 考虑如何结合上一节学到的传感器数据来实现简单的避障功能。

扩展阅读

在下一节中,我们将学习如何使用摄像头进行视觉识别,这将为我们的机器人添加更高级的感知能力。记住,优秀的工程师总是不断尝试和学习。继续探索,继续创新!

7.5 视觉识别与处理

想象一下,如果你的机器人能够"看到"并理解它所看到的东西,会有多么神奇!在这一节中,我们将探索计算机视觉的世界,让我们的机器人能够识别物体、检测颜色,甚至读取简单的标志。

计算机视觉基础概念

在我们开始编程之前,让我们先了解一些基本概念:

  1. 图像表示:计算机中的图像是由像素组成的二维数组。每个像素包含颜色信息,通常是RGB(红、绿、蓝)值。
  2. 图像处理:这包括对图像进行各种操作,如调整亮度、对比度,应用滤镜等。
  3. 特征提取:从图像中提取有用的信息,如边缘、角点、颜色分布等。
  4. 物体检测:在图像中定位并识别特定的物体。
  5. 机器学习在视觉中的应用:使用训练好的模型来识别复杂的物体或场景。

实践项目:实现基本的物体检测

我们将使用OpenCV库来实现一个简单的颜色检测和物体跟踪系统。

步骤1:环境设置

首先,我们需要安装OpenCV库:

pip install opencv-python numpy

步骤2:基本的颜色检测

让我们编写一个程序来检测特定颜色的物体:

import cv2
import numpy as np

# 初始化摄像头
cap = cv2.VideoCapture(0)

while True:
    # 读取一帧图像
    ret, frame = cap.read()
    if not ret:
        break

    # 转换到HSV颜色空间
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # 定义蓝色的HSV范围
    lower_blue = np.array([100, 50, 50])
    upper_blue = np.array([130, 255, 255])

    # 创建掩码
    mask = cv2.inRange(hsv, lower_blue, upper_blue)

    # 对原图像进行按位与操作
    res = cv2.bitwise_and(frame, frame, mask=mask)

    # 显示结果
    cv2.imshow('Original', frame)
    cv2.imshow('Mask', mask)
    cv2.imshow('Result', res)

    # 按'q'键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放摄像头并关闭所有窗口
cap.release()
cv2.destroyAllWindows()

这个程序会检测蓝色物体。你可以通过调整lower_blue和upper_blue的值来检测其他颜色。

步骤3:物体跟踪

现在,让我们扩展程序来跟踪检测到的物体:

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    lower_blue = np.array([100, 50, 50])
    upper_blue = np.array([130, 255, 255])

    mask = cv2.inRange(hsv, lower_blue, upper_blue)

    # 对掩码进行形态学操作,去除噪点
    kernel = np.ones((5, 5), np.uint8)
    mask = cv2.erode(mask, kernel, iterations=2)
    mask = cv2.dilate(mask, kernel, iterations=2)

    # 寻找轮廓
    contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 如果找到了轮廓
    if contours:
        # 找到最大的轮廓
        c = max(contours, key=cv2.contourArea)

        # 计算轮廓的重心
        M = cv2.moments(c)
        if M["m00"] != 0:
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])

            # 在图像上画出重心
            cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1)

            # 打印物体的位置
            print(f"Object at ({cx}, {cy})")

    cv2.imshow('Frame', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

这个程序能在当前图像中定位满足颜色阈值的最大连通区域。它并不具备通用的“物体理解”能力,光照变化和相似颜色都会影响结果。

挑战与思考

  1. 尝试检测不同的颜色。你需要如何修改代码?
  2. 如何让程序同时检测多种颜色?
  3. 思考:我们如何利用物体的位置信息来控制机器人的运动?
  4. 挑战:能否实现一个简单的"跟随物体"功能,让机器人跟随特定颜色的物体移动?

扩展阅读

下一节将把视觉实验与路径规划仿真区分开来:先离线验证感知结果,再讨论它进入真实控制回路所需的安全条件。

拓展:从视觉模型到机器人动作

若使用预训练检测器或交通标志分类器,教材必须同时给出模型来源、许可证、文件校验值、类别表和与训练一致的预处理流程。仅把整幅摄像头画面缩放后分类,不能可靠地定位交通标志;超声波测得的“最近反射面”也不一定是视觉框中的目标。因此,本章不把这两类结果直接接入电机控制。可将保存的视频作为离线输入,比较颜色阈值、检测置信度和误报率;任何真实运动实验都要另行完成标定、失效保护和独立急停验收。

7.6 导航与路径规划

想象一下,如果你的机器人能够自主地在复杂环境中穿行,避开障碍物,并找到最佳路径到达目标位置,那将是多么令人兴奋!在这一节中,我们将探索导航与路径规划的基础知识,并实现一个简单但功能强大的自主导航系统。

路径规划算法简介

在开始编程之前,让我们先了解一些基本的路径规划概念和算法:

  1. 图搜索算法:将环境表示为一个图,然后使用搜索算法找到最佳路径。 * 深度优先搜索(DFS) * 广度优先搜索(BFS) * A*算法(结合了最佳优先搜索和启发式搜索)
  2. 基于采样的算法:在连续空间中随机采样点,构建路径。 * 快速随机探索树(RRT) * 概率路线图(PRM)
  3. 势场法:将目标点视为吸引力源,障碍物视为斥力源。

在本节中,我们将重点关注A*算法,因为它在效率和最优性之间取得了很好的平衡。

实践项目:实现简单的自主导航

我们将创建一个简化的 2D 网格世界,在其中实现 A* 路径规划和基本的避障功能。

步骤1:创建环境表示

首先,我们需要创建一个表示我们环境的类:

class GridWorld:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.obstacles = set()

    def add_obstacle(self, x, y):
        self.obstacles.add((x, y))

    def is_valid_position(self, x, y):
        return 0 <= x < self.width and 0 <= y < self.height and (x, y) not in self.obstacles

    def get_neighbors(self, x, y):
        neighbors = [(x+1, y), (x-1, y), (x, y+1), (x, y-1)]
        return [(nx, ny) for nx, ny in neighbors if self.is_valid_position(nx, ny)]

步骤2:实现A*算法

现在,让我们实现A*算法:

import heapq

def heuristic(a, b):
    return abs(b[0] - a[0]) + abs(b[1] - a[1])

def a_star(grid, start, goal):
    if not grid.is_valid_position(*start) or not grid.is_valid_position(*goal):
        return None

    closed = set()
    came_from = {}
    g_score = {start: 0}
    open_set = [(heuristic(start, goal), 0, start)]

    while open_set:
        _, current_g, current = heapq.heappop(open_set)
        if current in closed:
            continue

        if current == goal:
            path = []
            while current in came_from:
                path.append(current)
                current = came_from[current]
            path.append(start)
            path.reverse()
            return path

        closed.add(current)

        for neighbor in grid.get_neighbors(*current):
            if neighbor in closed:
                continue
            tentative_g = current_g + 1
            if tentative_g < g_score.get(neighbor, float("inf")):
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g
                f_score = tentative_g + heuristic(neighbor, goal)
                heapq.heappush(open_set, (f_score, tentative_g, neighbor))

    return None

步骤3:在网格仿真中验证路径

下面的代码只在终端中显示规划结果,不控制真实机器人:

def render_grid(grid, path, start, goal):
    path_cells = set(path or [])
    for y in range(grid.height):
        row = []
        for x in range(grid.width):
            pos = (x, y)
            if pos == start:
                row.append("S")
            elif pos == goal:
                row.append("G")
            elif pos in grid.obstacles:
                row.append("#")
            elif pos in path_cells:
                row.append("*")
            else:
                row.append(".")
        print(" ".join(row))

grid = GridWorld(10, 10)
for obstacle in [(2, 2), (3, 3), (4, 4)]:
    grid.add_obstacle(*obstacle)

start, goal = (0, 0), (9, 9)
path = a_star(grid, start, goal)
assert path is not None
assert path[0] == start and path[-1] == goal
assert not (set(path) & grid.obstacles)
render_grid(grid, path, start, goal)

这条实验链可在普通Python环境中复现:构造网格、运行A*、用断言检查路径,再观察文本地图。把网格路径变成真实底盘动作还需要朝向、轮编码器或定位系统以及闭环控制,不能用固定时长的电机动作代替位姿更新。

挑战与思考

  1. 如何改进当前的导航系统以处理动态障碍物?
  2. 思考:A*算法在大规模环境中的性能如何?有什么可能的优化方法?
  3. 挑战:能否实现一个简单的SLAM(同时定位与地图构建)系统,让机器人在未知环境中导航?
  4. 考虑如何将不确定性纳入到路径规划中,以处理传感器噪声和环境变化。

扩展阅读

本节完成了一个可复现的网格路径规划实验。它验证的是规划算法,不代表实体机器人已经获得定位、避障或自主运动能力。

记住,真正的挑战往往在于如何处理现实世界的不确定性和复杂性。继续探索和实验,你可能会发现解决这些挑战的创新方法!

从规划仿真到实体系统

本节的A*示例假设地图、起点和终点都已知,且每次移动成本相同。动态障碍、实时建图与视觉语义导航属于拓展任务。它们需要定位误差模型、重规划频率、碰撞检测、看门狗和急停测试;本章不提供可直接驱动实体机器人的伪实现。

7.7 决策与行为控制

决策层把任务目标和感知状态转化为行为。常见方法包括有限状态机和行为树。行为树由条件、动作、顺序节点和选择节点组成;节点每次“滴答”返回成功(SUCCESS)、失败(FAILURE)或运行中(RUNNING)。

纯仿真实验:用决策规则选择行为

下面的示例不导入GPIO,也不会驱动电机。它用字典模拟机器人状态,便于先验证“安全优先”的决策顺序:

from enum import Enum, auto

class Status(Enum):
    SUCCESS = auto()
    FAILURE = auto()
    RUNNING = auto()

def choose_action(state):
    if state["emergency_stop"]:
        return "STOP"
    if state["distance_cm"] is None or state["distance_cm"] < 30:
        return "STOP"
    if state["battery_percent"] < 20:
        return "RETURN_TO_CHARGE"
    if state["target_visible"]:
        return "APPROACH_TARGET"
    return "EXPLORE"

tests = [
    ({"emergency_stop": True,  "distance_cm": 100, "battery_percent": 80, "target_visible": True}, "STOP"),
    ({"emergency_stop": False, "distance_cm": 20,  "battery_percent": 80, "target_visible": False}, "STOP"),
    ({"emergency_stop": False, "distance_cm": 100, "battery_percent": 10, "target_visible": True}, "RETURN_TO_CHARGE"),
    ({"emergency_stop": False, "distance_cm": 100, "battery_percent": 80, "target_visible": True}, "APPROACH_TARGET"),
]
for state, expected in tests:
    assert choose_action(state) == expected
print("all decision tests passed")

真实系统还必须规定每个动作的持续条件、超时、抢占和停止语义。例如,“前进”通常应返回RUNNING,并在条件失效时立即由更高优先级的安全分支抢占;程序退出时必须显式停止电机并释放资源。上述代码只验证决策逻辑,不是实体机器人控制程序。

挑战与思考

  1. 为传感器超时、通信中断和定位失效分别添加测试。
  2. 如果“电量低”和“前方有障碍”同时发生,为什么应该先停车?
  3. 画出等价的行为树,并标注每个节点可能返回的状态。

7.8 学习与适应

强化学习通过环境反馈学习策略。本节只在离散网格中演示Q-learning,不把训练中的随机探索用于真实电机控制。

纯仿真实验:固定迷宫中的Q-learning

import numpy as np

ACTIONS = [(0, -1), (0, 1), (-1, 0), (1, 0)]  # 上、下、左、右
WIDTH = HEIGHT = 6
START, GOAL = (0, 0), (5, 5)
OBSTACLES = {(1, 0), (1, 1), (1, 2), (3, 3), (4, 3)}

def step(state, action):
    dx, dy = ACTIONS[action]
    candidate = (state[0] + dx, state[1] + dy)
    valid = (
        0 <= candidate[0] < WIDTH
        and 0 <= candidate[1] < HEIGHT
        and candidate not in OBSTACLES
    )
    if not valid:
        return state, -5.0, False
    if candidate == GOAL:
        return candidate, 20.0, True
    return candidate, -1.0, False

rng = np.random.default_rng(42)
q = np.zeros((WIDTH, HEIGHT, len(ACTIONS)))
alpha, gamma, epsilon = 0.1, 0.95, 1.0

for _ in range(3000):
    state = START
    for _ in range(200):  # 防止无界循环
        if rng.random() < epsilon:
            action = int(rng.integers(len(ACTIONS)))
        else:
            action = int(np.argmax(q[state]))
        next_state, reward, done = step(state, action)
        target = reward if done else reward + gamma * np.max(q[next_state])
        q[state][action] += alpha * (target - q[state][action])
        state = next_state
        if done:
            break
    epsilon = max(0.05, epsilon * 0.995)

# 评估时关闭探索,并限制步数
state, path = START, [START]
for _ in range(100):
    action = int(np.argmax(q[state]))
    state, _, done = step(state, action)
    path.append(state)
    if done:
        break

assert path[-1] == GOAL, "策略未到达终点;请检查环境或训练参数"
print(path)

固定随机种子、固定地图、每回合最大步数和关闭探索的评估,使结果可以复查。障碍碰撞会得到惩罚并保持原位,目标奖励在到达时可达。

从仿真到真实机器人

持续学习和DQN需要经验回放、目标网络、稳定性评估和安全约束。真实机器人上的在线随机探索可能造成碰撞或设备损坏,因此本章删除不完整的实体部署伪代码。若继续拓展,应先在独立仿真器中训练和回归测试,再以冻结策略、低速沙箱和人工急停方式验证;不能把“仿真中得分提高”等同于真实系统安全。

挑战与思考

  1. 将Q-learning路径与A*路径长度比较,并解释两者所需先验信息的差异。
  2. 改变奖励后,记录成功率和平均步数,而不是只展示一次轨迹。
  3. 构造一个无解地图,验证最大步数能否让训练和评估正常结束。

7.9 项目整合与展示

必做项目:可复现的网格导航实验

学生提交一个不连接实体电机的导航程序,完成以下闭环:

  1. 使用固定网格、起点、终点和障碍物;
  2. 运行A*并用断言验证路径合法性;
  3. 运行安全优先的决策规则测试;
  4. 任选Q-learning作为对照,报告成功率、平均步数和随机种子;
  5. 提交环境版本、运行命令、预期输出和失败案例。

评价以正确性与可复现性(40%)、测试与异常处理(30%)、实验分析(20%)、表达与协作(10%)为准。

选做项目:低速实体避障

只有在教师审核接线和风险清单后才可进行。验收门槛包括:HC-SR04 ECHO电平转换、统一引脚表、保险丝/总开关、独立急停、测距超时即停车、程序异常退出即停车,以及架空轮测试通过。选做项目不要求视觉识别、SLAM、持续学习或DQN,避免把多个尚未验证的模块一次性接入执行器。

展示时应区分“已运行并测量的结果”“仿真结果”和“设计设想”,不得用含pass、未定义接口或随机数占位的框架冒充完成系统。

7.10 未来展望与伦理考虑

随着我们深入探索智能机器人系统的各个方面,我们不仅要思考技术本身,还要考虑这些技术将如何塑造我们的未来,以及我们作为技术开发者和使用者应该承担的责任。在本节中,我们将探讨智能机器人技术的前沿发展,并深入讨论相关的伦理和社会问题。

智能机器人的前沿发展

1. 人工通用智能(AGI)

目前的机器人系统大多是针对特定任务设计的。未来的发展方向之一是创造具有通用智能的机器人,能够像人类一样灵活地应对各种任务和环境。

2. 群体机器人和分布式智能

未来的机器人系统可能不再是独立的个体,而是相互协作的群体。

3. 软体机器人和仿生技术

模仿生物结构和行为的机器人设计正在兴起。

4. 脑机接口与人机融合

直接连接人脑和机器的技术正在快速发展。

5. 自主性和道德决策

赋予机器人更高水平的自主性和道德决策能力。

伦理与社会问题讨论

1. 就业和经济影响

2. 隐私和数据安全

3. 安全性和责任归属

4. 人机关系和社会影响

5. 军事应用和武器自主性

6. 公平性和偏见

案例研究:自动驾驶汽车的伦理困境

自动驾驶技术是智能机器人系统在现实世界中的一个重要应用,它集中体现了许多技术和伦理挑战。

场景:一辆自动驾驶汽车面临不可避免的碰撞,它必须在撞向一群行人或牺牲车内乘客之间做出选择。

讨论问题:

  1. 汽车应该如何"决定"?是否应该有预设的伦理准则?
  2. 这种决策应该由谁来制定—— 政府、汽车制造商、还是消费者?
  3. 如果自动驾驶汽车总体上比人类驾驶更安全,但偶尔会做出有争议的决定,社会应该如何看待?
  4. 这种情况下的责任归属问题应该如何解决?

小组讨论:智能机器人对未来社会的影响

为了深入思考这些问题,我们将进行一个小组讨论活动。

活动安排:

  1. 将班级分成 4-5 人的小组。
  2. 每组选择一个主题(如就业影响、隐私问题、人机关系等)。
  3. 小组内部讨论 20 分钟,准备一个简短的陈述。
  4. 每组派代表进行 5 分钟的陈述。
  5. 全班开放讨论 15 分钟。

评估标准:

结语

智能机器人技术正在以前所未有的速度发展,它带来了巨大的机遇,同时也伴随着重大的挑战和伦理问题。作为未来的工程师和科学家,你们不仅需要掌握先进的技术,还要培养对社会责任的认识和伦理思考的能力。

技术并非脱离价值而独立存在:需求定义、数据选择、性能指标和部署制度都会影响不同群体。希望通过本章的学习,你们不仅掌握构建智能机器人系统的基础知识,也能识别设计选择中的责任与权衡。