TT Lab
开始
学习 学习路径 课程

电子电路入门 — 传感器输入验证

温室风扇的振动呈现为另一个频率

在 TT Lab 中继续学习

目标

重现温室风扇 875Hz 的振动看起来像是 125Hz 的情形,并通过改变模拟滤波器和观测率,汇报可以区分的范围。

为什么重要

请在掌握前面的 RC、ADC 采集时间单元,以及 Python 函数、list、dict、异常、文件处理、复数、三角函数之后再进行。本实验预计需要 120 分钟。请在默认的 60 分钟会话结束之前,用“+时间”延长。最多 180 分钟,结束后文件会消失。请另行保存代码和报告。不会连接真实设备,也不会施加电压。

环境与产出物

要编写的文件只有 /root/aliasing/analyze.py 一个。镜像中带有 Python3 和 ngspice42,不需要外部下载或额外权限。请把对应的文件夹加入 sys.path 之后,再 import /opt/lab/fixtures/aliasing/aliaswave.py。这个辅助程序的 capture(cfg,新文件夹) 会运行真实的电路,并返回 新文件夹/trace.tsv 的路径。同时还会留下 input.cir、config.json、solver.log。父文件夹必须已存在,并且不会覆盖已有的文件夹。

cfg 是恰好包含 3 个键的 dict。input_hz 是 125、375、625、875 之一,cutoff_hz 是 None、300、80 之一,dt_s 是 2e-6、1e-6 之一。数值上拒绝 bool、字符串、NaN、无穷大。电路是 1V 的余弦输入、1kΩ 的串联电阻,以及 C=1/(2π1000fc) 的接地电容。如果是 None,则用 10¹²Ω 的负载代替 C。计算整个 90ms,并同时记录输入和输出。这个电路中不包含前一单元的采集开关模型。

提供的 load_trace(path) 会验证最大 16MiB 的波形,并返回由时间、输入、输出三列 tuple 组成的 list。表头是 time v(in) v(out),数据为 2 到 200000 行。时间不小于 0 且严格递增,第一个时间≤1µs,最后一个时间必须与 90ms 在 1e-12 秒以内。所有值都是有限的,电压绝对值≤1e6V。损坏为 ValueError,操作系统的文件错误原样传递。resample 接收通过这项验证的 rows,所以不需要每次调用都重复文件检查。

函数数值契约

明确指定的数值只允许 int 或 float,bool、字符串、NaN、无穷大、超出 float 转换范围,都是 ValueError。不要修改原始输入和原始文件。返回 dict 的键如下,不要添加不必要的字段。

函数 输入范围与返回
alias_hz 0≤freq≤1e9,0
resample 100≤fs≤8000,start≥0,count 为严格的 int 2 到 256;时刻 start+i/fs 的输出电压列表
spectrum values 为严格的 list,偶数长度 2 到 256,每个电压绝对值≤1e6;100≤fs≤8000
rc_gain 0≤freq≤1e9,cutoff=None 时为 1,否则 1e-6≤cutoff≤1e9
rc_window 1e-6≤wanted

spectrum 的每一项是 bin(从 0 到 N/2)、hz=k*fs/N、amplitude_v。Z=(1/N)Σvalues[i]exp(−2jπki/N),幅度在 k=0 或 N/2 时是 abs(Z),其余是 2abs(Z)。并列的峰值选择第一个频点。

analyze 只接收 fs=1000 或 4000。默认以起点 0.024 秒、64 个采样点进行分析,返回的键有 fs_hz、start_s、n、samples_v、spectrum、peak_hz、peak_amplitude_v。peak_hz 是最大幅度的频点频率,但如果该幅度≤1e-6V,则为 None。即使在这种情况下,peak_amplitude_v 也要保留真实的最大值。不要把文件读取错误随意变成无信号。

compare_reports 接收正常的 analyze 报告。如果 fs_hz、start_s、n 中有任何一个不同,或者 samples_v 的长度与 n 不同,或者为空,则为 ValueError。返回的 max_sample_difference_v 是采样点之间绝对差的最大值,indistinguishable 是该值≤1e-4V 的 bool。不要只比较幅度谱。

rc_gain 是 1/sqrt(1+(freq/cutoff)2)。rc_window 返回的 min_cutoff_hz=wanted/sqrt(1/min_gain2−1),max_cutoff_hz=nuisance/sqrt(1/max_gain**2−1),feasible=(下限≤上限)。feasible=False 也是正常结果,不要通过改变要求使它变成 True。

assess_filter 接收两个正常的 analyze 报告,并分别确认 peak_amplitude_v 是不小于 0 的有限数值。返回的键有 wanted_amplitude_v、nuisance_amplitude_v、passband_ok(第一个幅度≥.9)、stopband_ok(第二个≤.1)、all_pass(两个条件的 AND)。这个判定是 1V 输入、整数周期观测这一本实验的契约。

综合报告与 CLI 契约

campaign(inputs,runner) 的 inputs 是严格的 list,只接受 [125,875] 或 [375,625]。元素遵循上面的数值规则。按顺序,前面的是期望的输入,后面的是干扰。先验证全部输入,再调用 runner(cfg)。runner 返回波形路径,异常传递给调用方。保留原始输入。

运行的顺序是:cutoff 依次为 None→300→80,在每个 cutoff 下依次为输入对的顺序,在每个输入下依次为 dt 2e-6→1e-6。共 12 次,对每个波形执行 analyze(path,1000) 和 analyze(path,4000)。6 个条件各自的 trials 项如下。

filter_results 按 cutoff 的顺序共有 3 个。每一项的键有 cutoff_hz、assessment(对两个输入 coarse.low 的 assess_filter)、fine_assessment(对两个输入 fine.low 的 assess_filter)、valid(两个输入都收敛并且模型一致)、eligible(valid,并且两个 assessment 的 all_pass 也都为 True)。

最上层返回的键有 schema=1、trials、filter_results、single_rc_window=rc_window(*inputs)、collision(对无滤波的两个输入的 coarse.low 做 compare_reports)、scope='ideal-rc-and-uniform-sampling-only'。如果没有单个 RC 候选,就把 eligible 原样保留为 False。不要用空的 trials 来隐藏错误。

main(argv) 接收两个参数:输入对的 JSON 文件,以及尚不存在的输出文件夹路径。先验证 JSON 和输入对,再创建输出文件夹。父文件夹必须已存在。按 runner 的调用顺序,在其下的 run-00..run-11 这些新文件夹中,调用提供的 aliaswave.capture。把 campaign 的结果以一行 JSON 输出到 stdout,并返回 0。参数、配置、文件、运行的 Exception,要输出一行 {"error":"analysis_failed"} 并返回 2,不要输出 traceback。已经生成的新证据,即使失败也不要删除。import 时不运行,只在直接运行时,才以 main 的返回值退出。

提供的输入示例是 /opt/lab/fixtures/aliasing/inputs.json。在最后一步要亲自运行时,请选择新的输出名称。重定向报告时,也要注意不要覆盖已有的资料。

python3 /root/aliasing/analyze.py /opt/lab/fixtures/aliasing/inputs.json /root/aliasing/trial-01 > /root/aliasing/report-01.json

步骤

  1. 快速信号获得的另一个名称:在 /root/aliasing/analyze.py 中编写 alias_hz(freq,fs)。通过 f mod fs 与第一奈奎斯特区间的对称,返回折叠后的频率。要处理多个区间、DC 和边界,并遵循下面的数值契约。
  2. 用观测时刻代替行号:在同一个文件中添加 resample(rows,fs,start=.024,count=64)。返回在已验证的波形上,对 start+i/fs 处的输出电压做线性插值的 list。包含端点,超出范围的观测以 ValueError 拒绝。
  3. 区分幅度加倍的频点:添加 spectrum(values,fs)。生成包含用 1/N 归一化的 DFT 中 k=0..N/2 的 bin、hz、amplitude_v 的 dict 列表。只有内部频点乘以 2,DC 和奈奎斯特为 1 倍。
  4. 用两个观测率重新读取同一个波形:添加 analyze(path,fs)。用提供的 load_trace 读取文件,把观测与 DFT 连起来,返回下面的报告契约。fs 是 1000 或 4000,实际的采样时刻也必须随之改变。
  5. 区分相同的频谱与相同的采样点:添加 compare_reports(a,b)。确认观测时钟的 3 个字段和采样点数之后,返回最大采样差,以及是否在 100µV 以内。不同的时钟或空的采样点为 ValueError。
  6. 计算不可能的滤波器要求:添加 rc_gain(freq,cutoff) 和 rc_window(wanted,nuisance,min_gain=.9,max_gain=.1)。在原始输入频率下计算增益,并返回两个频带要求的 fc 允许区间,以及是否存在交集。
  7. 追问是否也保护了期望的信号:添加 assess_filter(wanted,nuisance)。用两个 analyze 报告的最大幅度,分别判定期望信号≥0.9V、干扰≤0.1V,并返回两个条件的 AND。
  8. 把电路、时钟、收敛绑定为证据:添加 campaign(inputs,runner)、main(argv) 和 CLI 入口。按下面的调用顺序运行 12 个电路,并连接 24 次分析。区分收敛、模型、要求,并保留真实波形和 JSON 报告。

参考

评分会检查各步骤的函数和原始资料的保留。最后还会在临时文件夹中运行真实的 ngspice 和单独的 CLI,并把保存的波形与报告进行对照。只清理检查专用的文件夹,不会修改学员文件。辅助程序的输入限制,是为了明确实验范围,并不是所有 ADC 和滤波器的模型。真实电路板的噪声、抖动、量化、随温度变化的可靠性,不在验证范围内。

快速信号获得的另一个名称

在 /root/aliasing/analyze.py 中编写 alias_hz(freq,fs)。通过 f mod fs 与第一奈奎斯特区间的对称,返回折叠后的频率。要处理多个区间、DC 和边界,并遵循下面的数值契约。

只减一次观测率,与求余数是不同的。bool 在 Python 中的行为像整数,但不要把它当作配置数值接收。

用观测时刻代替行号

在同一个文件中添加 resample(rows,fs,start=.024,count=64)。返回在已验证的波形上,对 start+i/fs 处的输出电压做线性插值的 list。包含端点,超出范围的观测以 ValueError 拒绝。

rows 的列依次是时间、输入、输出。请把恰好吻合某一行的情形,与位于两行之间的情形分开考虑。

区分幅度加倍的频点

添加 spectrum(values,fs)。生成包含用 1/N 归一化的 DFT 中 k=0..N/2 的 bin、hz、amplitude_v 的 dict 列表。只有内部频点乘以 2,DC 和奈奎斯特为 1 倍。

可以用恒定电压和符号交替变化的采样点,来确认端点的倍率。这个值是幅度,而不是功率密度。

用两个观测率重新读取同一个波形

添加 analyze(path,fs)。用提供的 load_trace 读取文件,把观测与 DFT 连起来,返回下面的报告契约。fs 是 1000 或 4000,实际的采样时刻也必须随之改变。

分析结果中的 peak_hz 是观测到的名称。不要说成找出了原始输入频率,对于无信号,也不要生成频率。

区分相同的频谱与相同的采样点

添加 compare_reports(a,b)。确认观测时钟的 3 个字段和采样点数之后,返回最大采样差,以及是否在 100µV 以内。不同的时钟或空的采样点为 ValueError。

即使幅度谱相同,相位也可能不同。必须在相同的时间点上比较电压。

计算不可能的滤波器要求

添加 rc_gain(freq,cutoff) 和 rc_window(wanted,nuisance,min_gain=.9,max_gain=.1)。在原始输入频率下计算增益,并返回两个频带要求的 fc 允许区间,以及是否存在交集。

RC 滤波器位于采样之前。不要用折叠后的频率计算增益,下限超过上限的结果也要原样保留。

追问是否也保护了期望的信号

添加 assess_filter(wanted,nuisance)。用两个 analyze 报告的最大幅度,分别判定期望信号≥0.9V、干扰≤0.1V,并返回两个条件的 AND。

即使减小了干扰,如果连期望的信号也丢掉了,就不满足设计要求。请同时保留观测值和判定字段。

把电路、时钟、收敛绑定为证据

添加 campaign(inputs,runner)、main(argv) 和 CLI 入口。按下面的调用顺序运行 12 个电路,并连接 24 次分析。区分收敛、模型、要求,并保留真实波形和 JSON 报告。

空的候选可能是正常的设计结论。不要把运行异常变成空的成功列表,也不要覆盖已有的证据文件夹。