1. CAN总线物理层波形观测与位时间参数测量
任务说明
本案例通过 CAN 分析仪(或示波器)采集 CAN_H、CAN_L 与差分电压,测量显性/隐性电平、位时间、边沿时间,并计算波特率误差。核心是:把采集到的原始采样点转换为可判定的位时间与波特率,并给出误差判定。
环境与依赖
- Python 3.8+,仅用标准库(
csv、statistics)。 - 输入:CSV 文件,列
t_us,can_h,can_l,时间单位微秒,电压单位伏特。 - 采集方式:示波器导出 CSV,或 CAN 分析仪(如 PCAN-View、CANoe、USB-CAN 工具)导出带 CAN_H/CAN_L 的采样数据。
测量原理
CAN 差分电压 Vdiff = CAN_H - CAN_L。显性位 Vdiff 约 2V,隐性位约 0V。用阈值 0.9V 判定逻辑电平,统计连续同电平采样段得到位时间,取所有位时间中位数作为位时间估计,波特率 = 1 / 位时间。误差 = (实测波特率 - 标称波特率) / 标称波特率。
完整代码
import csv, statistics, sys
V_TH = 0.9 # 显性/隐性判定阈值(V)
NOMINAL_BAUD = 500000 # 标称波特率
def load(path):
rows = []
with open(path, newline='') as f:
for r in csv.DictReader(f):
rows.append((float(r['t_us']), float(r['can_h']), float(r['can_l'])))
return rows
def measure(rows, nominal=NOMINAL_BAUD):
# 1. 差分电压与逻辑电平
samples = [(t, h - l) for t, h, l in rows]
levels = [(t, 1 if v >= V_TH else 0) for t, v in samples]
# 2. 显性/隐性电平统计
dom = [v for _, v in samples if v >= V_TH]
rec = [v for _, v in samples if v < V_TH]
# 3. 提取连续同电平段作为位时间
bit_times = []
start = levels[0][0]
cur = levels[0][1]
for t, lv in levels[1:]:
if lv != cur:
bit_times.append(t - start)
start, cur = t, lv
if len(bit_times) < 2:
raise ValueError("采样点不足,无法提取位时间")
bit_t = statistics.median(bit_times) * 1e-6 # us -> s
baud = 1.0 / bit_t
err = (baud - nominal) / nominal
# 4. 边沿时间:10%~90% 上升/下降
def edge_time(idx):
v0, v1 = samples[idx-1][1], samples[idx][1]
lo, hi = min(v0, v1), max(v0, v1)
t10 = t90 = None
for t, v in samples[idx-1:idx+1]:
if t10 is None and v >= lo + 0.1*(hi-lo): t10 = t
if t90 is None and v >= lo + 0.9*(hi-lo): t90 = t
return None if t10 is None or t90 is None else abs(t90 - t10)
edges = [edge_time(i) for i in range(1, len(samples))
if (samples[i][1] >= V_TH) != (samples[i-1][1] >= V_TH)]
edges = [e for e in edges if e is not None]
return {
"dominant_v": statistics.mean(dom) if dom else None,
"recessive_v": statistics.mean(rec) if rec else None,
"bit_time_us": bit_t * 1e6,
"baud": baud,
"error_pct": err * 100,
"edge_us": statistics.mean(edges) if edges else None,
}
if __name__ == "__main__":
path = sys.argv[1] if len(sys.argv) > 1 else "can_wave.csv"
res = measure(load(path))
print(f"显性电平: {res['dominant_v']:.3f} V")
print(f"隐性电平: {res['recessive_v']:.3f} V")
print(f"位时间: {res['bit_time_us']:.3f} us")
print(f"波特率: {res['baud']:.0f} bps")
print(f"波特率误差: {res['error_pct']:.3f} %")
print(f"平均边沿时间: {res['edge_us']:.3f} us" if res['edge_us'] else "边沿时间: 无")
运行与验证
python can_measure.py can_wave.csv
CSV 示例(每行一个采样点):
t_us,can_h,can_l
0,2.5,2.5
1,3.5,1.5
2,3.5,1.5
3,2.5,2.5
判定标准:显性电平约 2V、隐性约 0V;位时间与标称位时间(500kbps 为 2us)接近;波特率误差绝对值小于 0.5% 视为通过。若误差超限,检查采样率是否足够(建议 ≥ 10 倍波特率)以及阈值设置。