首页/在线代码库/demo10_spatial_stack.py

demo10_spatial_stack.py

1. fig_si_stack.png 完整技术栈分层(7 层, 标注文档覆盖情况) 2. fig_si_loop.png 感知->理解->记忆->推理->行动 闭环 3. fig_si_semantic.png 语义层: 几何 -> 标签 -> 场景图 4. fig_si_temporal.png 时序层: 静态帧 -> 光流/跟踪/SLAM 4D

Python 167 行 系列:技术栈 🔒 源码仅供在线学习,不提供文件下载
完整源码 · 在线查看demo10_spatial_stack.py
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
"""空间智能全景概念图。

1. fig_si_stack.png    完整技术栈分层(7 层, 标注文档覆盖情况)
2. fig_si_loop.png     感知->理解->记忆->推理->行动 闭环
3. fig_si_semantic.png 语义层: 几何 -> 标签 -> 场景图
4. fig_si_temporal.png 时序层: 静态帧 -> 光流/跟踪/SLAM 4D
"""
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch
from matplotlib.patches import Circle, Rectangle
from figure_style import figures_dir, configure

configure()

OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)

# ============ 1. 完整技术栈(7 层) ============
fig, ax = plt.subplots(figsize=(12, 7))
ax.set_xlim(0, 12); ax.set_ylim(0, 9.6); ax.axis("off")
ax.set_title("补全后的空间智能技术栈 (Spatial Intelligence Stack)", fontsize=15, pad=12)

# 层定义: (y 位置, 层名, 内容, 状态颜色, 状态文本)
layers = [
    (0.3, "Layer 0 · 图元感知\nPerception", "霍夫变换 · RANSAC · 边缘检测 · 角点提取", "tab:gray", "文档①覆盖"),
    (1.7, "Layer 1 · 拓扑结构\nTopology", "线框解析 · 最小环基 · 子图同构 · 矢量化", "tab:gray", "文档②覆盖"),
    (3.1, "Layer 2 · 三维重建\n3D Reconstruction", "点线面体 · 三角测量 · 伪交点破除 · B-rep", "tab:gray", "文档③覆盖"),
    (4.5, "Layer 3 · 表示与记忆\nRepresentation", "NeRF/3DGS · 语义地图 · 分层空间记忆 · 隐式场", "tab:orange", "仅触及 B-rep/SDF"),
    (5.9, "Layer 4 · 时序与动态\nTemporal", "SLAM · 目标跟踪 · 4D重建 · 动态场景流", "tab:red", "本文档未覆盖"),
    (7.3, "Layer 5 · 推理与语义\nReasoning", "场景图 · 空间关系 · Affordance · 开放词汇理解", "tab:red", "本文档未覆盖"),
    (8.7, "Layer 6 · 决策与行动\nAction", "路径规划 · 机器人操作 · 人机交互 · 物理仿真", "tab:red", "本文档未覆盖"),
]
for y, name, content, color, status in layers:
    ax.add_patch(FancyBboxPatch((0.4, y), 3.4, 1.1, boxstyle="round,pad=0.03",
                                fc=color, ec="k", alpha=0.85))
    ax.text(2.1, y + 0.55, name, ha="center", va="center", fontsize=10.5,
            color="white", fontweight="bold")
    ax.add_patch(FancyBboxPatch((4.0, y), 6.6, 1.1, boxstyle="round,pad=0.03",
                                fc="white", ec=color, lw=1.5))
    ax.text(7.3, y + 0.62, content, ha="center", va="center", fontsize=9.5)
    ax.text(10.9, y + 0.55, status, ha="center", va="center", fontsize=9,
            color="k", style="italic")
fig.tight_layout()
fig.savefig(os.path.join(OUT, "fig_si_stack.png"), dpi=150)
plt.close(fig)

# ============ 2. 感知->理解->记忆->推理->行动 闭环 ============
fig, ax = plt.subplots(figsize=(11, 4.2))
ax.set_xlim(0, 12); ax.set_ylim(0, 4.5); ax.axis("off")
ax.set_title("空间智能的完整闭环: 感知 → 理解 → 记忆 → 推理 → 行动", fontsize=14)

stages = [
    (0.5, "感知\nPerception", "像素→图元\n(霍夫/RANSAC)", "tab:blue"),
    (2.9, "理解\nUnderstanding", "结构/语义\n(拓扑/场景图)", "tab:green"),
    (5.3, "记忆\nMemory", "空间地图\n(NeRF/3DGS)", "tab:orange"),
    (7.7, "推理\nReasoning", "关系/规划\n(路径/Affordance)", "tab:purple"),
    (10.1, "行动\nAction", "机器人操作\n(抓取/导航)", "tab:red"),
]
for x, title, sub, color in stages:
    ax.add_patch(FancyBboxPatch((x, 1.4), 1.7, 1.6, boxstyle="round,pad=0.05",
                                fc=color, ec="k", alpha=0.85))
    ax.text(x + 0.85, 2.6, title, ha="center", fontsize=10.5,
            color="white", fontweight="bold")
    ax.text(x + 0.85, 1.9, sub, ha="center", fontsize=8.5, color="white")
for i in range(len(stages) - 1):
    ax.add_patch(FancyArrowPatch((stages[i][0] + 1.7, 2.2), (stages[i + 1][0], 2.2),
                                 arrowstyle="-|>", mutation_scale=18, lw=2, color="k"))
# 反馈回路(行动回到感知)
ax.add_patch(FancyArrowPatch((10.1 + 0.85, 1.3), (1.35, 1.3),
                             arrowstyle="-|>", mutation_scale=18, lw=1.5,
                             color="gray", ls="--", connectionstyle="arc3,rad=-0.15"))
ax.text(5.8, 0.55, "行动反馈修正感知(主动视觉 / Next-Best-View)", ha="center",
        fontsize=9, color="gray")
fig.tight_layout()
fig.savefig(os.path.join(OUT, "fig_si_loop.png"), dpi=150)
plt.close(fig)

# ============ 3. 语义层: 几何 -> 标签 -> 场景图 ============
fig, axes = plt.subplots(1, 3, figsize=(12.5, 4))
ax = axes[0]; ax.set_xlim(0, 10); ax.set_ylim(0, 6); ax.axis("off")
ax.set_title("① 几何图元", fontsize=10)
# 画一间房(墙) + 窗 + 门(几何)
ax.add_patch(Rectangle((1, 1), 8, 4, fill=False, lw=2))
ax.add_patch(Rectangle((2.5, 1), 2, 0.8, fill=False, ec="tab:blue", lw=2))
ax.add_patch(Rectangle((5, 1), 1.6, 0.7, fill=False, ec="tab:orange", lw=2))
ax.text(3.5, 3.0, "矩形环", fontsize=8, ha="center", color="tab:blue")
ax.text(5.8, 3.0, "矩形环", fontsize=8, ha="center", color="tab:orange")
ax.text(1.2, 1.2, "轮廓", fontsize=8)

ax = axes[1]; ax.set_xlim(0, 10); ax.set_ylim(0, 6); ax.axis("off")
ax.set_title("② 语义标签", fontsize=10)
ax.add_patch(Rectangle((1, 1), 8, 4, fill="lightblue", alpha=0.3, lw=2))
ax.add_patch(Rectangle((2.5, 1), 2, 0.8, fc="tab:blue", alpha=0.5))
ax.add_patch(Rectangle((5, 1), 1.6, 0.7, fc="tab:orange", alpha=0.5))
ax.text(3.5, 2.4, "窗 window", fontsize=8.5, ha="center", color="tab:blue")
ax.text(5.8, 2.4, "门 door", fontsize=8.5, ha="center", color="tab:orange")
ax.text(2.2, 4.6, "墙 wall", fontsize=8.5, color="k")

ax = axes[2]; ax.set_xlim(0, 10); ax.set_ylim(0, 6); ax.axis("off")
ax.set_title("③ 场景图 Scene Graph", fontsize=10)
nodes = {"room": (5, 5.0), "wall": (2, 3.2), "window": (3.4, 1.3), "door": (6.6, 1.3)}
rels = [("room", "wall", "contains"), ("wall", "window", "has"), ("wall", "door", "has"),
        ("room", "door", "connects-to")]
for a, b, rel in rels:
    ax.annotate("", xy=nodes[b], xytext=nodes[a],
                arrowprops=dict(arrowstyle="-|>", color="k", lw=1.2))
    ax.text((nodes[a][0] + nodes[b][0]) / 2 + 0.4, (nodes[a][1] + nodes[b][1]) / 2,
            rel, fontsize=6.5, color="gray")
for name, (x, y) in nodes.items():
    ax.add_patch(Circle((x, y), 0.55, fc="tab:green", ec="k"))
    ax.text(x, y, name, ha="center", va="center", fontsize=8, color="white")
ax.set_xlim(0, 10); ax.set_ylim(0, 6)
fig.suptitle("语义层: 从几何形状到「这是什么 / 有什么关系」", fontsize=14)
fig.tight_layout(rect=(0, 0, 1, 0.93))
fig.savefig(os.path.join(OUT, "fig_si_semantic.png"), dpi=150)
plt.close(fig)

# ============ 4. 时序层: 静态帧 -> 光流/跟踪 -> SLAM ============
fig, axes = plt.subplots(1, 3, figsize=(12.5, 4))
rng = np.random.default_rng(5)

# ① 连续两帧(箭头 = 光流)
ax = axes[0]; ax.set_xlim(0, 10); ax.set_ylim(0, 6); ax.axis("off")
ax.set_title("① 相邻帧光流 (RAFT)", fontsize=10)
pts = rng.uniform(1, 9, (12, 2))
for p in pts:
    ax.plot(*p, ".", c="k", ms=6)
    d = np.array([0.8, 0.3]) + rng.normal(0, 0.15, 2)
    ax.annotate("", xy=p + d, xytext=p, arrowprops=dict(arrowstyle="-|>", color="tab:red", lw=1.4))
ax.text(5, 5.5, "每个像素的运动向量", fontsize=8, ha="center", color="tab:red")

# ② 目标跟踪
ax = axes[1]; ax.set_xlim(0, 10); ax.set_ylim(0, 6); ax.axis("off")
ax.set_title("② 跨帧目标跟踪", fontsize=10)
t = np.linspace(0, 1, 5)
traj = np.array([1 + 7 * t, 4 - 2 * t]).T
for i, p in enumerate(traj):
    ax.add_patch(Circle(p, 0.32, fc="tab:blue", ec="k", alpha=0.5 + 0.1 * i))
    ax.text(p[0], p[1] + 0.55, f"t{i}", ha="center", fontsize=8)
ax.plot(*traj.T, "tab:blue", lw=1, alpha=0.5)
ax.text(5, 0.6, "同一物体身份跨帧保持 (ID 一致性)", fontsize=8, ha="center")

# ③ SLAM 地图
ax = axes[2]; ax.set_xlim(0, 10); ax.set_ylim(0, 6); ax.axis("off")
ax.set_title("③ SLAM: 相机轨迹 + 稀疏地图", fontsize=10)
t = np.linspace(0, 2 * np.pi, 40)
cam = np.array([5 + 2.5 * np.cos(t), 3 + 1.6 * np.sin(t)]).T
ax.plot(*cam.T, color="tab:gray", lw=1.5, label="相机轨迹")
ax.scatter(cam[::3, 0], cam[::3, 1], s=8, c="tab:red", marker="s", label="相机位姿")
mp = rng.uniform(1.5, 8.5, (26, 2))
ax.scatter(mp[:, 0], mp[:, 1], s=3, c="k", alpha=0.7)
ax.text(5, 5.5, "稀疏路标点(可增量更新)", fontsize=8, ha="center")
ax.legend(fontsize=7, loc="lower right")

fig.suptitle("时序与动态层: 从静态快照到 4D 世界", fontsize=14)
fig.tight_layout(rect=(0, 0, 1, 0.93))
fig.savefig(os.path.join(OUT, "fig_si_temporal.png"), dpi=150)
plt.close(fig)

print("生成 4 张空间智能全景概念图:")
for f in ["fig_si_stack.png", "fig_si_loop.png", "fig_si_semantic.png", "fig_si_temporal.png"]:
    p = os.path.join(OUT, f)
    print(f"  {p} ({os.path.getsize(p)} bytes)")

输出图

fig_si_loop.png
输出图 · fig_si_loop.png
fig_si_semantic.png
输出图 · fig_si_semantic.png
fig_si_stack.png
输出图 · fig_si_stack.png
fig_si_temporal.png
输出图 · fig_si_temporal.png