371 lines
15 KiB
Python
371 lines
15 KiB
Python
import math
|
||
from decimal import Decimal, ROUND_HALF_EVEN
|
||
|
||
"""
|
||
Wheatstone Bridge Resistance Measurement Calculator
|
||
电桥法测量电阻计算器
|
||
|
||
参考了表面张力系数计算器的高精度与不确定度修约逻辑。
|
||
|
||
公式说明:
|
||
1. 惠斯通电桥平衡条件 (Bridge Balance Equation):
|
||
当检流计示数为零时,电桥平衡:
|
||
Rx / R0 = R1 / R2
|
||
即:
|
||
Rx = R0 * (R1 / R2) (也写作 Rx = R0 * C,C = R1/R2 为比例臂比值)
|
||
|
||
其中:
|
||
Rx : 待测电阻
|
||
R0 : 比较臂标准电阻(电桥读数)
|
||
R1/R2 : 比例臂之比,取 1:1, 1:10, 10:1
|
||
|
||
2. 灵敏度公式 (Bridge Sensitivity):
|
||
S = Δn / (ΔR0 / R0)
|
||
|
||
其中:
|
||
S : 电桥灵敏度(格/相对变化量)
|
||
Δn : 检流计偏转格数(通常取 5 小格)
|
||
ΔR0 : 使检流计偏转 Δn 格时 R0 的改变量
|
||
ΔR0 = |R0' - R0|,R0' 为偏转 5 格时的读数
|
||
R0 : 平衡时的标准电阻读数
|
||
|
||
也可写为:
|
||
S = Δn * R0 / ΔR0
|
||
|
||
3. 不确定度传递公式 (Uncertainty Propagation):
|
||
Rx = R0 * C (C 为比例臂比值)
|
||
|
||
(a) R0 的不确定度:
|
||
u(R0) = sqrt( u_A(R0)^2 + u_B(R0)^2 )
|
||
其中:
|
||
u_A(R0) = s(R0) / sqrt(n) (A 类不确定度, s 为标准偏差)
|
||
u_B(R0) = Δ_inst / sqrt(3) (B 类不确定度, Δ_inst 为仪器误差限)
|
||
|
||
(b) 仪器误差限 Δ_inst 的确定 (与灵敏度有关):
|
||
Δ_inst = R0 / S
|
||
即由灵敏度决定的电桥最小可分辨电阻变化量
|
||
|
||
(c) 比例臂 C 视为精确值(标准电阻箱),则:
|
||
u(Rx) = C * u(R0)
|
||
|
||
(d) 相对不确定度:
|
||
u_r(Rx) = u(Rx) / Rx = u(R0) / R0
|
||
|
||
(e) 修约规则:
|
||
不确定度保留一位有效数字,四舍六入五凑偶
|
||
Rx 末位与不确定度对齐
|
||
"""
|
||
|
||
def scientific_round(value, uncertainty):
|
||
"""
|
||
按照"四舍六入五凑偶"修约:
|
||
1. 不确定度 u 保留一位有效数字
|
||
2. 平均值末位与 u 对齐
|
||
"""
|
||
if uncertainty <= 0:
|
||
return str(value), "0"
|
||
|
||
u_float = float(uncertainty)
|
||
first_digit_pos = math.floor(math.log10(u_float))
|
||
prec = Decimal('1e' + str(first_digit_pos))
|
||
|
||
u_final = uncertainty.quantize(prec, rounding=ROUND_HALF_EVEN)
|
||
val_final = value.quantize(prec, rounding=ROUND_HALF_EVEN)
|
||
|
||
return val_final, u_final
|
||
|
||
def calculate_stats(data_list, delta_inst):
|
||
"""
|
||
计算一组数据的平均值、A类、B类及合成不确定度
|
||
u(X) = sqrt( u_A^2 + u_B^2 )
|
||
u_A = s / sqrt(n)
|
||
u_B = Δ_inst / sqrt(3)
|
||
"""
|
||
n = len(data_list)
|
||
mean = sum(data_list) / Decimal(n)
|
||
|
||
if n > 1:
|
||
variance = sum((x - mean)**2 for x in data_list) / Decimal(n - 1)
|
||
s = Decimal(str(math.sqrt(float(variance))))
|
||
u_a = s / Decimal(str(math.sqrt(n)))
|
||
else:
|
||
u_a = Decimal("0")
|
||
|
||
u_b = Decimal(delta_inst) / Decimal(str(math.sqrt(3)))
|
||
u_combined = Decimal(str(math.sqrt(float(u_a**2 + u_b**2))))
|
||
|
||
return mean, u_a, u_b, u_combined
|
||
|
||
def input_decimal(prompt, default=None):
|
||
"""输入一个 Decimal 值,支持默认值"""
|
||
while True:
|
||
try:
|
||
suffix = f" (默认 {default})" if default else ""
|
||
val = input(f"{prompt}{suffix}: ").strip()
|
||
if not val and default is not None:
|
||
return Decimal(str(default))
|
||
if not val:
|
||
continue
|
||
return Decimal(val)
|
||
except Exception:
|
||
print("输入无效,请输入数字。")
|
||
|
||
def input_int(prompt, default=None):
|
||
"""输入一个整数,支持默认值"""
|
||
while True:
|
||
try:
|
||
suffix = f" (默认 {default})" if default else ""
|
||
val = input(f"{prompt}{suffix}: ").strip()
|
||
if not val and default is not None:
|
||
return default
|
||
return int(val)
|
||
except Exception:
|
||
print("输入无效,请输入整数。")
|
||
|
||
def measure_one_ratio(ratio_label, C_value, R_nominal, n_measurements):
|
||
"""
|
||
对单一比例臂进行测量与分析
|
||
|
||
参数:
|
||
ratio_label: 比例臂标签,如 "1:1"
|
||
C_value: 比例臂比值 C = R1/R2
|
||
R_nominal: 标定电阻标称值 (Ω)
|
||
n_measurements: 测量次数
|
||
"""
|
||
C = Decimal(str(C_value))
|
||
|
||
print(f"\n{'─'*50}")
|
||
print(f" 比例臂 Rx:R0 = {ratio_label},比例因子 C = {C_value}")
|
||
print(f" 标定电阻标称值: {R_nominal} Ω")
|
||
print(f"{'─'*50}")
|
||
|
||
# --- 输入平衡时的 R0 读数 ---
|
||
print(f"\n请输入 {n_measurements} 次平衡时的 R0 读数 (Ω):")
|
||
R0_data = []
|
||
for i in range(n_measurements):
|
||
while True:
|
||
try:
|
||
val = input(f" 第 {i+1} 次 R0 = ").strip()
|
||
if not val: continue
|
||
R0_data.append(Decimal(val))
|
||
break
|
||
except Exception:
|
||
print(" 输入无效,请输入数字。")
|
||
|
||
# --- 输入偏差 5 小格时的 R0' ---
|
||
print(f"\n请输入偏转 5 小格时的 R0' 读数 (Ω):")
|
||
R0_prime = input_decimal(" R0'")
|
||
delta_n = Decimal("5") # 检流计偏转格数
|
||
|
||
# --- 计算 R0 的平均值 ---
|
||
R0_mean = sum(R0_data) / Decimal(len(R0_data))
|
||
|
||
# --- 计算灵敏度 ---
|
||
# S = Δn * R0 / |R0' - R0|
|
||
delta_R0 = abs(R0_prime - R0_mean)
|
||
if delta_R0 == 0:
|
||
print(" ⚠ 警告: R0' = R0,无法计算灵敏度,请检查数据!")
|
||
S = Decimal("Infinity")
|
||
delta_inst = Decimal("0")
|
||
else:
|
||
S = delta_n * R0_mean / delta_R0
|
||
# 仪器误差限 (由灵敏度决定)
|
||
# Δ_inst = R0 / S = |R0' - R0| / Δn = ΔR0 / 5
|
||
delta_inst = R0_mean / S
|
||
|
||
# --- 不确定度分析 ---
|
||
R0_mean_final, u_a_R0, u_b_R0, u_R0 = calculate_stats(R0_data, str(delta_inst))
|
||
|
||
# Rx = C * R0
|
||
Rx_mean = C * R0_mean_final
|
||
u_Rx = C * u_R0
|
||
|
||
# 相对不确定度
|
||
if Rx_mean != 0:
|
||
u_r_Rx = u_Rx / Rx_mean
|
||
else:
|
||
u_r_Rx = Decimal("0")
|
||
|
||
# --- 修约 ---
|
||
Rx_final, u_Rx_final = scientific_round(Rx_mean, u_Rx)
|
||
|
||
# --- 输出结果 ---
|
||
print(f"\n{'═'*50}")
|
||
print(f" 结果 (比例臂 {ratio_label}, 标定 {R_nominal} Ω)")
|
||
print(f"{'═'*50}")
|
||
|
||
# 打印 R0 测量数据表
|
||
print(f"\n R0 测量数据:")
|
||
for i, val in enumerate(R0_data):
|
||
print(f" 第 {i+1} 次: {val} Ω")
|
||
|
||
print(f"\n R0 平均值 = {R0_mean_final:.4f} Ω")
|
||
print(f" R0' = {R0_prime} Ω")
|
||
print(f" ΔR0 = |R0'-R0| = {delta_R0:.4f} Ω")
|
||
print(f" 偏转格数 Δn = {delta_n} 格")
|
||
print(f"{'─'*50}")
|
||
print(f" 灵敏度 S = Δn·R0/ΔR0 = {S:.2f}")
|
||
print(f" 仪器误差限 Δ_inst = R0/S = {delta_inst:.4f} Ω")
|
||
print(f"{'─'*50}")
|
||
print(f" u_A(R0) = {u_a_R0:.6f} Ω")
|
||
print(f" u_B(R0) = {u_b_R0:.6f} Ω")
|
||
print(f" u(R0) = {u_R0:.6f} Ω")
|
||
print(f"{'─'*50}")
|
||
print(f" Rx = C × R0 = {C} × {R0_mean_final:.4f} = {Rx_mean:.4f} Ω")
|
||
print(f" u(Rx) = C × u(R0) = {u_Rx:.6f} Ω")
|
||
print(f" 相对不确定度 u_r = {u_r_Rx*100:.2f}%")
|
||
print(f"{'─'*50}")
|
||
print(f" ★ 最终结果: Rx = {Rx_final} ± {u_Rx_final} Ω")
|
||
print(f"{'═'*50}")
|
||
|
||
return {
|
||
"ratio_label": ratio_label,
|
||
"C": C,
|
||
"R_nominal": R_nominal,
|
||
"R0_data": R0_data,
|
||
"R0_mean": R0_mean_final,
|
||
"R0_prime": R0_prime,
|
||
"delta_R0": delta_R0,
|
||
"S": S,
|
||
"delta_inst": delta_inst,
|
||
"u_a_R0": u_a_R0,
|
||
"u_b_R0": u_b_R0,
|
||
"u_R0": u_R0,
|
||
"Rx_mean": Rx_mean,
|
||
"u_Rx": u_Rx,
|
||
"u_r_Rx": u_r_Rx,
|
||
"Rx_final": Rx_final,
|
||
"u_Rx_final": u_Rx_final
|
||
}
|
||
|
||
def main():
|
||
print("════════════════════════════════════════════════")
|
||
print(" 电桥法测量电阻计算器 (Wheatstone Bridge)")
|
||
print("════════════════════════════════════════════════")
|
||
print()
|
||
print("实验原理:")
|
||
print(" 惠斯通电桥平衡条件: Rx = R0 × C (C = R1/R2)")
|
||
print(" 灵敏度: S = Δn × R0 / ΔR0")
|
||
print(" 不确定度: u(Rx) = C × u(R0), u(R0) = √(u_A² + u_B²)")
|
||
print()
|
||
|
||
# --- 1. 基本参数 ---
|
||
n_meas = input_int("每组测量次数 n", default=5)
|
||
|
||
# --- 2. 定义测量方案 ---
|
||
# 标定电阻: 10kΩ 和 2kΩ
|
||
# 比例臂: 1:1, 1:10, 10:1
|
||
# 注意: 2kΩ 不会有 1:10 的比例臂 (因为 R0 = Rx/(C) 会超出电阻箱量程)
|
||
|
||
calibration_plan = [
|
||
# (标定电阻标称值, 比例臂标签, C 值)
|
||
# ---- 10kΩ 标定 ----
|
||
("10000", "1:1", 1),
|
||
("10000", "10:1", 10),
|
||
# ---- 2kΩ 标定 ----
|
||
("2000", "1:1", 1),
|
||
("2000", "10:1", 10),
|
||
# ---- 200Ω 标定 ----
|
||
("200", "1:1", 1),
|
||
("200", "1:10", 0.1),
|
||
("200", "10:1", 10),
|
||
]
|
||
|
||
print(f"\n{'━'*55}")
|
||
print(" 测量方案如下:")
|
||
print(f"{'━'*55}")
|
||
print(f" {'序号':<6}{'标定电阻':>10}{'比例臂 Rx:R0':>14}{'C = R1/R2':>12}")
|
||
print(f" {'─'*46}")
|
||
for idx, (R_nom, ratio, C_val) in enumerate(calibration_plan, 1):
|
||
print(f" {idx:<6}{R_nom + ' Ω':>10}{ratio:>14}{C_val:>12}")
|
||
print(f"{'━'*55}")
|
||
print()
|
||
print(" 注: 2kΩ 标定不进行 1:10 比例臂测量")
|
||
print(" (R0 = 2000/0.1 = 20000 Ω,超出电阻箱量程)")
|
||
print()
|
||
|
||
# --- 3. 逐项测量 ---
|
||
all_results = []
|
||
|
||
for idx, (R_nom, ratio_label, C_val) in enumerate(calibration_plan, 1):
|
||
print(f"\n{'▓'*55}")
|
||
print(f" 第 {idx} 组: 标定 {R_nom} Ω,比例臂 Rx:R0 = {ratio_label}")
|
||
print(f"{'▓'*55}")
|
||
|
||
result = measure_one_ratio(ratio_label, C_val, R_nom, n_meas)
|
||
all_results.append(result)
|
||
|
||
if idx < len(calibration_plan):
|
||
input("\n按回车继续下一组测量...")
|
||
|
||
# --- 4. 汇总表 ---
|
||
print("\n")
|
||
print("╔══════════════════════════════════════════════════════════════════════════════╗")
|
||
print("║ 测 量 结 果 汇 总 表 ║")
|
||
print("╠══════════════════════════════════════════════════════════════════════════════╣")
|
||
|
||
header = (f" {'标定':>6} {'比例臂':>8} {'R0均值(Ω)':>12} "
|
||
f"{'S(灵敏度)':>10} {'Δ_inst(Ω)':>10} "
|
||
f"{'Rx(Ω)':>12} {'u(Rx)(Ω)':>10} {'u_r(%)':>8}")
|
||
print(header)
|
||
print(f" {'─'*74}")
|
||
|
||
for res in all_results:
|
||
S_str = f"{res['S']:.1f}" if res['S'] != Decimal("Infinity") else "∞"
|
||
line = (f" {res['R_nominal']:>6} {res['ratio_label']:>8} "
|
||
f"{res['R0_mean']:>12.4f} {S_str:>10} "
|
||
f"{res['delta_inst']:>10.4f} "
|
||
f"{res['Rx_mean']:>12.4f} {res['u_Rx']:>10.6f} "
|
||
f"{res['u_r_Rx']*100:>7.2f}%")
|
||
print(line)
|
||
|
||
print("╚══════════════════════════════════════════════════════════════════════════════╝")
|
||
|
||
# --- 5. 灵敏度分析总结 ---
|
||
print("\n")
|
||
print("┌──────────────────────────────────────────────┐")
|
||
print("│ 灵 敏 度 分 析 总 结 │")
|
||
print("├──────────────────────────────────────────────┤")
|
||
|
||
for res in all_results:
|
||
S_str = f"{res['S']:.2f}" if res['S'] != Decimal("Infinity") else "∞"
|
||
print(f"│ 标定 {res['R_nominal']:>5}Ω 比例臂 {res['ratio_label']:>4} "
|
||
f"S = {S_str:>8} │")
|
||
|
||
print("├──────────────────────────────────────────────┤")
|
||
|
||
# 找灵敏度最大和最小
|
||
finite_results = [r for r in all_results if r['S'] != Decimal("Infinity")]
|
||
if finite_results:
|
||
best = max(finite_results, key=lambda r: r['S'])
|
||
worst = min(finite_results, key=lambda r: r['S'])
|
||
print(f"│ 最高灵敏度: 标定{best['R_nominal']:>5}Ω 比例臂{best['ratio_label']:>4} │")
|
||
print(f"│ S = {best['S']:.2f} │")
|
||
print(f"│ 最低灵敏度: 标定{worst['R_nominal']:>5}Ω 比例臂{worst['ratio_label']:>4} │")
|
||
print(f"│ S = {worst['S']:.2f} │")
|
||
|
||
print("├──────────────────────────────────────────────┤")
|
||
print("│ 分析: │")
|
||
print("│ · 灵敏度 S 越大,测量精度越高 │")
|
||
print("│ · S = Δn·R0/ΔR0 │")
|
||
print("│ · 比例臂比值影响灵敏度 │")
|
||
print("│ · 合理选择比例臂可优化测量精度 │")
|
||
print("└──────────────────────────────────────────────┘")
|
||
|
||
# --- 6. 最终修约结果汇总 ---
|
||
print("\n")
|
||
print("╔═══════════════════════════════════════════════════╗")
|
||
print("║ 最 终 结 果 (修约后) ║")
|
||
print("╠═══════════════════════════════════════════════════╣")
|
||
|
||
for res in all_results:
|
||
print(f"║ 标定 {res['R_nominal']:>5}Ω 比例臂 {res['ratio_label']:>4}: ║")
|
||
print(f"║ Rx = {res['Rx_final']} ± {res['u_Rx_final']} Ω ║")
|
||
print(f"║ (相对不确定度 {res['u_r_Rx']*100:.2f}%) ║")
|
||
print(f"╟───────────────────────────────────────────────────╢")
|
||
|
||
print("╚═══════════════════════════════════════════════════╝")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|