3、环境感知与状态表示:传感器抽象、环境建模方法、状态空间表示、部分可观测环境处理

好,咱们进入第三章。这一章聊的是智能体怎么「看」世界、怎么「理解」世界。

说白了,智能体不是人。它没有眼睛,没有耳朵。它只能靠传感器数据来感知环境。但传感器数据往往是原始、嘈杂、不完整的。怎么把这些乱七八糟的数据变成智能体能用的「状态」?这就是本章要解决的问题。

我在做第一个机器人项目时,就踩过这个坑。传感器数据哗哗地来,我直接拿原始数据去决策,结果机器人像个无头苍蝇。后来才明白——你得先给环境建模,把感知抽象成状态。

3.1 传感器抽象:把物理信号变成信息

传感器种类太多了。摄像头、激光雷达、温度计、麦克风、GPS……每种传感器输出的数据格式都不一样。你不可能为每种传感器都写一套决策逻辑。

所以我们需要「传感器抽象层」。它的作用很简单:把不同传感器的原始数据,统一成智能体能理解的格式

核心思想: 不管底层是什么传感器,上层只关心「环境中有哪些物体、它们的状态是什么」。

举个例子。一个避障机器人,可能有超声波传感器、红外传感器、激光雷达。传感器抽象层要做的是:

  • 读取超声波的距离值(比如 1.2 米)
  • 读取红外的障碍物标志(比如 true)
  • 读取激光雷达的点云数据

然后统一输出成:{"前方障碍物": true, "距离": 1.2, "置信度": 0.85}

我习惯用接口类来做这件事。定义一个抽象基类,所有传感器都继承它:

class Sensor:
    def read(self) -> dict:
        """返回统一格式的感知数据"""
        pass

class UltrasonicSensor(Sensor):
    def read(self):
        distance = self._get_raw_distance()
        return {
            "type": "distance",
            "value": distance,
            "unit": "meter",
            "confidence": 0.9 if distance < 5 else 0.6
        }

class CameraSensor(Sensor):
    def read(self):
        image = self._capture()
        objects = self._detect_objects(image)
        return {
            "type": "object_list",
            "value": objects,
            "confidence": 0.75
        }

这样做的好处很明显:决策层不用关心你用的是超声波还是激光雷达。它只看到统一的 read() 输出。换传感器?改一行配置就行。

我的经验: 传感器抽象层一定要加「置信度」字段。我在项目中遇到过,超声波在雨天测距完全不准,但代码不知道。加了置信度后,决策层可以忽略低置信度的数据,系统鲁棒性提升了一大截。

3.2 环境建模方法:怎么描述世界

传感器数据来了,但怎么组织这些数据?这就涉及到环境建模。

环境建模,说白了就是用数据结构来描述智能体所处的世界。不同的任务,建模方式完全不同。

我常用的几种建模方法:

3.2.1 栅格地图(Grid Map)

把环境划分成一个个小格子。每个格子标记为「空闲」「占用」「未知」。最适合扫地机器人、自动驾驶的局部路径规划。

grid_map = [
    [0, 0, 1, 0, 0],
    [0, 1, 1, 0, 0],
    [0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0]
]
# 0=空闲, 1=占用

3.2.2 拓扑图(Topological Map)

用节点和边来表示环境。节点是「关键位置」(比如房间、路口),边是「连通关系」。适合导航、送货机器人。

graph = {
    "A": {"B": 5, "C": 3},
    "B": {"A": 5, "D": 2},
    "C": {"A": 3, "D": 4},
    "D": {"B": 2, "C": 4}
}

3.2.3 语义地图(Semantic Map)

不仅知道「哪里有障碍物」,还知道「那是什么」。比如「这是一张桌子」「那是一把椅子」。适合人机交互、服务机器人。

semantic_map = {
    "objects": [
        {"type": "table", "position": (2.0, 3.0), "size": (1.2, 0.8)},
        {"type": "chair", "position": (2.5, 3.5), "size": (0.5, 0.5)},
        {"type": "door", "position": (0.0, 1.0), "state": "open"}
    ]
}

选型建议: 任务越简单,模型越简单。别一上来就用语义地图。我见过有人给一个「跟着线走」的机器人建了3D点云地图……杀鸡用牛刀了。

3.3 状态空间表示:智能体的「大脑模型」

环境建模完了,但智能体不能直接拿地图去决策。它需要「状态」——也就是当前这一刻,它对环境的完整认知。

状态空间,就是所有可能状态的集合。每个状态,是智能体对环境的「快照」。

举个例子。一个简单的温控智能体:

  • 传感器:温度计
  • 环境:房间
  • 状态:当前温度值(比如 25°C)
  • 状态空间:所有可能的温度值(比如 0°C ~ 50°C)

状态表示有两种常见方式:

表示方式 说明 适用场景
离散状态 状态是有限的、可枚举的 棋类游戏、有限状态机
连续状态 状态是连续的数值 机器人控制、自动驾驶
混合状态 部分离散、部分连续 大多数真实场景

我习惯用字典或类来表示状态:

class RobotState:
    def __init__(self):
        self.position = (0.0, 0.0)      # 连续:位置
        self.velocity = 0.0              # 连续:速度
        self.battery = 100               # 连续:电量
        self.mode = "exploring"          # 离散:模式
        self.obstacle_detected = False   # 离散:是否检测到障碍物

    def to_dict(self):
        return {
            "position": self.position,
            "velocity": self.velocity,
            "battery": self.battery,
            "mode": self.mode,
            "obstacle": self.obstacle_detected
        }

避坑指南: 状态空间别设计得太大。我曾经给一个导航机器人设计了 50 个状态变量,结果强化学习训练了三天都没收敛。后来砍到 8 个关键变量,半天就搞定了。状态表示要「够用就好」。

3.4 部分可观测环境处理:当你看不到全局

现实世界有个残酷的事实:智能体永远无法获得环境的完整信息

你想想看,一个自动驾驶汽车,能看到拐角后面的车吗?不能。一个扫地机器人,能知道隔壁房间有没有垃圾吗?不能。

这就是「部分可观测环境」(POMDP)。智能体只能看到环境的一部分,但必须基于不完整的信息做决策。

怎么处理?我常用的方法有四种:

3.4.1 维护信念状态(Belief State)

不直接相信传感器数据,而是维护一个「概率分布」。比如:

  • 传感器说「前方有障碍物」,但置信度只有 60%
  • 那么信念状态就是:P(有障碍物) = 0.6, P(无障碍物) = 0.4
class BeliefState:
    def __init__(self):
        self.beliefs = {}  # 每个变量的概率分布

    def update(self, sensor_data, confidence):
        # 贝叶斯更新
        for key, value in sensor_data.items():
            old_belief = self.beliefs.get(key, 0.5)
            new_belief = old_belief * confidence + (1 - old_belief) * (1 - confidence)
            self.beliefs[key] = new_belief

    def get_most_likely(self):
        return {k: v > 0.5 for k, v in self.beliefs.items()}

3.4.2 记忆机制(Memory)

把历史观测存下来。虽然现在看不到,但之前看到过。比如机器人转过弯后,还记得刚才那个房间的布局。

class Memory:
    def __init__(self):
        self.history = []

    def remember(self, state):
        self.history.append(state)
        if len(self.history) > 100:
            self.history.pop(0)

    def recall(self, key):
        # 从历史中查找某个信息
        for state in reversed(self.history):
            if key in state:
                return state[key]
        return None

3.4.3 主动感知(Active Perception)

如果信息不够,就主动去获取。比如机器人停下来,转头看看盲区。或者无人机飞高一点,获得更广的视野。

3.4.4 鲁棒决策(Robust Decision)

实在看不清,就做「最坏情况假设」。比如:不确定前方有没有坑,就默认有坑,绕路走。虽然保守,但安全。

注意: 部分可观测环境下,最忌讳的是「过度自信」。我见过一个案例,机器人只看到半张桌子,就认定「整条路都被堵死了」,结果原地转圈。后来加了信念状态,它知道「可能被堵,也可能没被堵」,决策就灵活多了。

3.5 实战:搭建一个简单的感知-状态流水线

最后,咱们把今天的内容串起来。一个完整的感知-状态流水线长这样:

class PerceptionPipeline:
    def __init__(self):
        self.sensors = []          # 传感器列表
        self.environment_model = None  # 环境模型
        self.state = None          # 当前状态
        self.belief = BeliefState()    # 信念状态

    def add_sensor(self, sensor):
        self.sensors.append(sensor)

    def step(self):
        # 1. 读取所有传感器
        raw_data = {}
        for sensor in self.sensors:
            data = sensor.read()
            raw_data.update(data)

        # 2. 传感器抽象:统一格式
        abstracted = self._abstract(raw_data)

        # 3. 更新环境模型
        self.environment_model.update(abstracted)

        # 4. 更新信念状态(处理部分可观测)
        self.belief.update(abstracted, confidence=0.8)

        # 5. 生成当前状态
        self.state = {
            "position": self.environment_model.get_position(),
            "obstacles": self.belief.get_most_likely(),
            "map": self.environment_model.get_local_map()
        }

        return self.state

    def _abstract(self, raw):
        # 抽象逻辑:提取关键信息
        return {
            "distance": raw.get("ultrasonic", 999),
            "objects": raw.get("camera_objects", []),
            "battery": raw.get("battery", 100)
        }

嗯,这一章内容不少。总结一下:

  • 传感器抽象:把原始数据变成统一格式
  • 环境建模:用合适的数据结构描述世界
  • 状态空间:定义智能体的「认知快照」
  • 部分可观测:用信念状态、记忆、主动感知来应对不确定性

下一章,咱们聊决策规划——有了状态,怎么决定下一步做什么。

一句话记住本章: 智能体不是上帝,它只能看到世界的一个小窗口。你的任务,就是帮它把这个小窗口里的信息,变成能用的知识。