UCB EE120 信号与系统自学指南:六个 Python 实验打通从傅里叶变换到滤波与控制的完整链路
本篇是 CS 自学指南(cs-self-learning)「电子基础 · 信号与系统」分支中 UCB EE120 课程页 的深度展开。EE120 全称 Signal and Systems,由 UC Berkeley 开设,它以扎实的傅里叶变换讲义与 6 个 Python 编程实验闻名:从手写 FFT、从手指影像测心率,到还原哈勃望远镜拍摄的星空、用反馈系统平衡小车上的细杆。读完本文,你将明确这门课的难度定位、先修链条、100 小时投入如何分配,以及每个核心实验背后的信号处理理论,从而判断它是否值得纳入你自己的自学路线。
课程档案一览
以下元数据直接取自仓库中的课程介绍文档 docs/电子基础/signal.en.md 及其中文版 signal.md:
| 项目 | 内容 |
|---|---|
| 所属大学 | UC Berkeley |
| 课程全称 | EE120: Signal and Systems |
| 先修要求 | CS61A、CS70、微积分、线性代数 |
| 编程语言 | Python |
| 课程难度 | ★★★★★ |
| 预计学时 | 100 小时 |
| 作业组成 | 5 个书面作业 + 6 个编程作业 |
在仓库中,这门课被归入「电子基础(Fundamental Electronics)」分类,与 EE16A&B(电路)、MIT 6.007 Signals and Systems 并列,导航结构见 mkdocs.yml。需要特别注意的是:EE120 在本仓库标记为满级五星难度,而同分类下的 MIT 6.007(Oppenheim 讲授的"远古版本")仅有二星、约 50–70 小时投入——两者定位差异明显,后面会详述如何按需取舍。
为什么把 EE120 放进自学路线
CS 学习规划 的「信号与系统」小节给出了作者的选课动机:
最初学它只是为了满足我对傅里叶变换的好奇,但学完之后我才不禁感叹,傅立叶变换给我提供了一个全新的视角去看待这个世界……让你沉浸在用数学去精确描绘和刻画这个世界的优雅与神奇之中。
同小节还明确指出:UCB EE120 关于傅里叶变换的 notes 写得非常好,并且提供了 6 个非常有趣的 Python 编程作业,让学习者"在实践中运用信号与系统的理论与算法"。这正是它区别于 MIT 6.003 / 6.007 等纯理论派课程的核心卖点:
- MIT 6.007(Signals_and_Systems_AVO)由 Alan V. Oppenheim 主讲,理论体系经典、难度温和,适合快速建立学科直觉;
- EE120 则把同样的理论(卷积、傅里叶变换、采样、滤波、反馈)落到"能用代码解决真实问题"的层面,适合已经具备编程能力、希望动手验证数学的人。
先修基础与仓库内对应课程
EE120 属于本科中高阶课程,对数学与编程都有明确要求。若基础未打牢,可以先用仓库中对应的自学资源补齐:
| EE120 要求 | 对应自学资源(仓库内) |
|---|---|
| CS61A(Python 编程) | UCB CS61A: Structure and Interpretation of Computer Programs |
| CS70(离散数学与概率) | UCB CS70: Discrete Math and Probability Theory |
| 微积分 | MIT 18.01 / 18.02 数学课程 |
| 线性代数 | MIT 18.06 线性代数 |
其中 Python 熟练度尤为重要:课程的 6 个实验都以 Python 为载体,实验里要手写 FFT、处理视频帧序列、批量操作图像矩阵,如果此前只写过脚本级代码,建议先用 CS61A 或等价课程把 numpy 生态(数组运算、向量化、fft/rfft 等)练熟。此外,如果希望先对"传感器采集 → 数据分析 → 预测反馈"整条链路有感性的体感,可以选修同分支下的 EE16A&B(伯克利电子系大一入门课,难度三星)作为热身,但这并非 EE120 的硬性前置。
学习节奏与课程结构
课程材料形态为:课堂讲义(notes)+ 课程录播 + 5 个书面作业 + 6 个编程作业(Lab),录播与作业均通过课程官网分发。EE120 的讲义质量在仓库中受到高度评价(见上节引文),建议的学习循环是:
- 先读对应章节 notes,抓住数学主线——信号如何表示、LTI 系统如何用卷积刻画、时域与频域如何互相转换;
- 再看录播补充推导细节,书面作业通常围绕变换公式、系统性质、采样定理等推导题展开;
- 最后用 Lab 把理论兑现成可运行的 Python 代码,并用可视化结果反哺理解。
关于 100 小时的时间预算,可以做一个粗略拆解:若每周投入 8–10 小时、持续约 10–13 周(大致一个学期的节奏),6 个 Lab 平均每个需要投入一周中的 2–3 个小时用于编码与调试,书面作业与 notes 阅读占剩余时间。考虑到它是五星满级难度,建议不要在课业繁忙期硬塞,留出连续、不被打断的编码窗口。
核心实验逐站拆解
课程介绍文档明确指出,这门课"最精华的部分就是 6 个超有趣的编程作业",并逐一给出了从 Lab3 到 Lab6 的剧透。仓库文档并未逐条描述 Lab1、Lab2 的内容,但从 Lab3 起就要求手写 FFT 可以推断,前两个实验重在循序渐进地引入用 Python/numpy 处理离散信号的基本工作流,为后续的"重头戏"铺路。下面把文档明示的四个核心实验逐一拆开,讲清它考察的理论点与背后的工程价值。
Lab 3:手写 FFT 并对标 numpy
文档任务描述:实现 FFT 算法,并与 Numpy 官方实现进行性能对比。
离散傅里叶变换(DFT)的朴素实现是 的矩阵乘,而 FFT 通过分治把复杂度降到 。自己实现一遍 FFT,才能真正理解蝶形运算、位反转与复数旋转因子这些概念,而不是把 np.fft.fft 当成黑盒。下面是还原这一核心对比的示意代码(非课程官方代码,仅演示二者的正确性与性能差距):
import numpy as np
import time
def dft(x):
"""按定义逐点实现的 DFT(O(N^2)),N 不宜取太大。"""
N = len(x)
n = np.arange(N).reshape(-1, 1) # 列向量
k = np.arange(N) # 行向量
W = np.exp(-2j * np.pi * n * k / N)
return W @ x
N = 2048
x = np.random.randn(N) + 1j * np.random.randn(N)
t0 = time.perf_counter()
y_naive = dft(x)
t1 = time.perf_counter()
y_fft = np.fft.fft(x)
t2 = time.perf_counter()
print(f"naive DFT: {t1 - t0:.3f}s | numpy FFT: {t2 - t1:.3f}s")
print("max abs error:", np.max(np.abs(y_naive - y_fft)))
运行后你会直观看到:结果一致(误差在浮点精度级别),但 FFT 的速度优势随着 增大呈数量级差距。这个实验的意义在于让"算法复杂度分析"落地——课堂上的 与真实耗时在这里互相印证。
Lab 4:从手指影像推断心率
文档任务描述:通过分析手指头的影像数据推断心率。
其背后的物理原理是光电容积描记(PPG):手指组织中的血流量随心跳周期性变化,会规律性地改变对光线的吸收,因此视频每一帧中手指区域的平均亮度会携带周期性的微小波动,这个波动的频率就是心率。处理管线通常是:
- 逐帧提取手指区域,计算区域平均亮度,得到一条随时间变化的一维信号;
- 去除直流分量与缓慢漂移(去趋势/去均值),必要时做带通滤波去掉呼吸等低频干扰;
- 对信号做 FFT,在合理的心率频带(约 0.8–3.0 Hz,对应 48–180 bpm)内找到幅度最大的频率分量,乘以 60 即得到每分钟心跳数。
下面是一段能说明核心思路的示意代码:
import numpy as np
def estimate_heart_rate(brightness, fps=30.0):
"""brightness: 每帧手指区域平均亮度构成的一维数组。"""
x = brightness - brightness.mean()
X = np.fft.rfft(x * np.hanning(len(x)))
freqs = np.fft.rfftfreq(len(x), d=1.0 / fps)
lo, hi = 0.8, 3.0 # 心跳频带:48 ~ 180 bpm
band = (freqs >= lo) & (freqs <= hi)
peak = freqs[band][np.argmax(np.abs(X[band]))]
return peak * 60.0 # 单位:bpm
这个实验是"采样定理 + 频谱分析"的绝佳实战:视频帧率就是采样率,若帧率过低导致心跳频率混叠,测出的心率就会出错——把抽象的采样/混叠概念变成肉眼可见的现象。
Lab 5:给哈勃望远镜的照片降噪
文档任务描述:对哈勃望远镜拍到的照片进行降噪处理,恢复绚烂清晰的星空。
深空天文图像的特点是有用的星点信号微弱、散布稀疏,而探测器噪声(光子计数涨落带来的泊松噪声、读出噪声等)相对显著。要在这种低信噪比下"找回星空",课程要求综合运用:
- 噪声统计建模:理解噪声的分布特性,判断哪些起伏是信号、哪些是噪声;
- 频域/空间滤波:星点具有近似已知的点扩散形态,与随机噪声在频域上可分,因此可以设计合适的滤波器在抑制噪声的同时保留星点能量;
- 基于先验的复原:结合"星空中大部分区域是暗背景,亮点是稀疏星点"这一先验,把复原问题转化为可求解的优化或检测问题。
这个 Lab 之所以被课程介绍文档评为"最牛的一个",在于它把一个看起来很"图像处理/天文学"的问题,拆解成了纯粹用信号与系统语言(噪声、滤波、频谱)就能解决的任务,做完之后对"降噪"二字会有本质层面的理解。
Lab 6:构造反馈系统平衡细杆
文档任务描述:构造一个反馈系统,平衡小车上的细杆。
这是经典的 cart-pole(倒立摆)问题,也是控制理论入门的标志性题目。系统由小车与铰接在其上的细杆构成:杆子天生不稳定,任何扰动都会让它倾倒,唯一的手段是测量当前状态(杆的倾角与角速度、小车的位置等),据此计算施加在小车上的力,构成闭环。核心概念包括:
- 系统的动态建模与传递函数/状态空间描述;
- 开环不稳定系统为何必须引入反馈;
- 反馈增益如何决定系统的稳定性与响应速度——增益过低压不住扰动,过高又会引发振荡。
前五个实验解决的多是"分析/复原"类问题(把信号拆开、把噪声去掉),到 Lab 6 则转向"设计/控制"类问题(让系统达到并维持期望状态)。这恰好呼应了 CS 学习规划 中对 EE16 系的描述——从"从环境中收集信息"到"做出预测反馈",EE120 用信号与系统的语言补上了理论拼图。
资源与配套材料
- 课程资料(notes / 录播 / 书面作业):均通过 UCB EE120 课程官网分发,仓库文档引用的学期为 Fall 2019(
inst.eecs.berkeley.edu/~ee120/fa19/),录播与作业具体入口参见课程官网对应栏目。 - 学习者的作业实现与资源汇总:课程介绍文档注明,@PKUFlyingPig 在学习这门课中用到的所有资源和作业实现都汇总维护在个人仓库 PKUFlyingPig/UCB-EE120 中。作为自学参考时请注意:参考他人的实现思路可以帮助卡点排障,但务必先独立完成实验,再对照差异,这样才能收获完整的调试与推导经验。
如何把它放进你自己的自学计划
结合 CS 学习规划 与 使用指南 对读者的分层,给出三条落地建议:
- 面向 CS 系统学习路线的读者:将 EE120 安排在完成编程入门(如 CS61A)与数学基础(微积分、线性代数,对应 MITmaths、MITLA)之后。它在仓库中的难度评级为五星、预算 100 小时,属于"需要专门腾出一段连续时间"的重课,建议放在假期或课业较轻的学期。
- 只想补齐核心知识、时间有限的读者:可以直接选择同分类下更轻量的 MIT 6.007(Oppenheim)(二星、50–70 小时)建立理论框架,EE120 的 6 个 Lab 则可以挑选其中最感兴趣的 1–2 个(如 Lab4 测心率)单独完成,作为对理论的自测。
- 对电路采集链路感兴趣的读者:可先学 EE16A&B 体验从传感器到算法的完整流水线,再进入 EE120,感受同一套"从信号到系统"的思维在不同硬件层级上的复用。
判断这门课是否适合自己,最简单的方式是翻一翻它的傅里叶变换讲义:如果你能被"用另一种视角重新描述世界"这件事打动,那么 EE120 的 100 小时几乎不会让你失望;如果你读完只觉枯燥,那同目录下的 MIT 系列或许是更匹配的起点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00