从光电效应实验到Python数据可视化:用Matplotlib复现普朗克常量测量全过程

当金属板在特定频率的光照射下突然逸出电子时,这个被称为"光电效应"的现象不仅颠覆了经典物理学的认知,更为量子理论奠定了基础。如今,我们不仅能通过实验验证这一现象,还能借助Python的强大计算和可视化能力,将传统物理实验转化为数字化的科学探索过程。

对于理工科学生和研究者而言,手动处理实验数据、绘制图表往往耗时耗力且容易出错。本文将展示如何运用Python生态中的NumPy和Matplotlib库,从原始实验数据出发,通过程序化处理、曲线拟合和可视化呈现,精确计算出普朗克常量,并生成专业级的实验图表。

1. 实验数据准备与预处理

光电效应实验的核心数据通常包括不同波长光的频率ν和对应的截止电压U₀。在传统实验中,这些数据被记录在表格中,而我们将首先将其转化为Python可处理的形式。

import numpy as np

# 实验原始数据:波长(nm), 频率(×10^14 Hz), 截止电压绝对值(V)
experiment_data = [
    (406, 7.39, 1.352),
    (450, 6.67, 1.053),
    (488, 6.15, 0.854),
    (519, 5.78, 0.714),
    (635, 4.72, 0.340)
]

# 将数据转换为NumPy数组
wavelengths = np.array([d[0] for d in experiment_data])
frequencies = np.array([d[1] for d in experiment_data]) * 1e14  # 转换为标准Hz单位
stopping_voltages = np.array([d[2] for d in experiment_data])

注意:频率单位需要统一为赫兹(Hz),而原始数据通常以×10^14 Hz记录,因此需要乘以1e14进行转换。

数据预处理阶段还需要考虑误差处理。光电效应实验中常见的误差来源包括:

  • 波长测量误差(约±2nm)
  • 电压表读数误差(约±0.01V)
  • 环境光干扰导致的电流测量误差

我们可以为这些误差创建对应的数组:

wavelength_errors = np.array([2] * len(wavelengths))  # 假设波长误差±2nm
voltage_errors = np.array([0.01] * len(stopping_voltages))  # 假设电压误差±0.01V

2. 线性拟合与普朗克常量计算

爱因斯坦光电效应方程表明,截止电压U₀与光频率ν之间存在线性关系:

U₀ = (h/e)ν - W₀/e

其中h是普朗克常量,e是电子电荷量,W₀是金属的逸出功。通过拟合U₀-ν直线的斜率,我们可以计算出普朗克常量h。

from scipy.stats import linregress

# 执行线性回归
slope, intercept, r_value, p_value, std_err = linregress(frequencies, stopping_voltages)

# 计算普朗克常量 (h = slope * e)
e = 1.602176634e-19  # 电子电荷量,单位C
h_calculated = slope * e
h_theoretical = 6.62607015e-34  # 理论普朗克常量,单位J·s

print(f"拟合得到的斜率: {slope:.3e} V/Hz")
print(f"计算得到的普朗克常量: {h_calculated:.3e} J·s")
print(f"理论普朗克常量: {h_theoretical:.3e} J·s")
print(f"相对误差: {abs(h_calculated-h_theoretical)/h_theoretical*100:.2f}%")

提示:良好的实验数据拟合结果通常相关系数(r_value)应大于0.99,表明线性关系显著。

为了评估拟合质量,我们可以计算决定系数(R²)和每个参数的误差:

# 计算决定系数
r_squared = r_value**2

# 计算斜率的标准误差
slope_error = std_err

# 计算普朗克常量的误差
h_error = slope_error * e

print(f"决定系数 R² = {r_squared:.5f}")
print(f"斜率误差: ±{slope_error:.3e} V/Hz")
print(f"普朗克常量误差: ±{h_error:.3e} J·s")

3. 专业级数据可视化实现

数据可视化是实验结果呈现的关键环节。使用Matplotlib,我们可以创建出版质量的图表来展示实验发现。

3.1 截止电压与频率关系图

import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator

plt.figure(figsize=(10, 6), dpi=300)

# 绘制数据点及误差条
plt.errorbar(frequencies/1e14, stopping_voltages, 
             xerr=wavelength_errors*1e14/406**2,  # 波长误差转换为频率误差
             yerr=voltage_errors,
             fmt='o', markersize=8, capsize=5, capthick=2,
             label='实验数据')

# 绘制拟合直线
fit_line = slope * frequencies + intercept
plt.plot(frequencies/1e14, fit_line/1e14, 'r--', 
         label=f'线性拟合 (斜率={slope:.3e} V/Hz)')

# 图表装饰
plt.xlabel('光频率 (×10¹⁴ Hz)', fontsize=12)
plt.ylabel('截止电压 (V)', fontsize=12)
plt.title('光电效应:截止电压与光频率关系', fontsize=14)
plt.grid(True, linestyle='--', alpha=0.6)
plt.legend(fontsize=10)

# 设置刻度
ax = plt.gca()
ax.xaxis.set_major_locator(MultipleLocator(0.5))
ax.yaxis.set_major_locator(MultipleLocator(0.2))

plt.tight_layout()
plt.show()

3.2 伏安特性曲线可视化

光电效应实验通常还测量不同光强下的伏安特性曲线。我们可以用三维图或子图方式展示这些数据:

# 伏安特性数据示例 (λ=450nm)
voltages = np.array([0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 22.0, 24.0, 26.0])
currents = np.array([
    [3.8, 14.9, 23.7, 29.4, 36.8, 43.1, 48.1, 52.2, 55.4, 58.9, 61.5, 64.5, 66.8, 68.9],
    [4.5, 17.5, 27.8, 34.4, 43.2, 50.6, 56.4, 61.2, 64.9, 69.1, 72.1, 75.7, 78.3, 80.6],
    [5.2, 20.1, 32.0, 39.6, 49.7, 58.3, 64.9, 70.5, 74.7, 79.4, 83.4, 87.0, 90.1, 92.8],
    [5.8, 22.5, 36.0, 44.5, 55.9, 65.5, 72.8, 79.1, 83.9, 89.1, 93.7, 97.7, 101.6, 104.1],
    [6.6, 24.6, 39.6, 49.2, 61.8, 72.4, 80.6, 87.4, 92.7, 98.6, 103.6, 108.1, 111.9, 115.1]
]) * 1e-10  # 转换为安培单位

plt.figure(figsize=(12, 7), dpi=300)

for i, current in enumerate(currents):
    plt.plot(voltages, current*1e9,  # 转回nA单位方便显示
             marker='o', linestyle='-', 
             label=f'光强档位 {i+1}')

plt.xlabel('外加电压 (V)', fontsize=12)
plt.ylabel('光电流 (nA)', fontsize=12)
plt.title('光电管伏安特性曲线 (λ=450nm)', fontsize=14)
plt.grid(True, linestyle='--', alpha=0.6)
plt.legend(title='光强档位:', fontsize=10, title_fontsize=11)
plt.xlim(0, 26)
plt.ylim(0, 120)

plt.tight_layout()
plt.show()

4. 实验报告自动化生成

将上述分析和可视化整合为可重复使用的Jupyter Notebook或Python脚本,可以大大简化实验报告编写过程。以下是一个报告生成函数的示例:

from datetime import datetime

def generate_experiment_report(data, output_file='photoelectric_effect_report.html'):
    """生成光电效应实验HTML报告"""
    
    # 执行数据分析
    wavelengths = np.array([d[0] for d in data])
    frequencies = np.array([d[1] for d in data]) * 1e14
    stopping_voltages = np.array([d[2] for d in data])
    
    slope, intercept, r_value, _, std_err = linregress(frequencies, stopping_voltages)
    h_calculated = slope * 1.602176634e-19
    
    # 创建图表
    plt.figure(figsize=(10, 6))
    plt.errorbar(frequencies/1e14, stopping_voltages, 
                 yerr=[0.01]*len(data), fmt='o', capsize=5)
    plt.plot(frequencies/1e14, (slope*frequencies + intercept)/1e14, 'r--')
    plt.xlabel('光频率 (×10¹⁴ Hz)')
    plt.ylabel('截止电压 (V)')
    plt.grid(True)
    plot_file = 'fit_plot.png'
    plt.savefig(plot_file, bbox_inches='tight')
    plt.close()
    
    # 生成HTML报告
    html = f"""
    <html>
    <head>
        <title>光电效应实验报告</title>
        <style>
            body {{ font-family: Arial, sans-serif; line-height: 1.6; max-width: 900px; margin: auto; padding: 20px; }}
            h1 {{ color: #2c3e50; border-bottom: 2px solid #3498db; }}
            .result {{ background: #f8f9fa; padding: 15px; border-radius: 5px; }}
            table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }}
            th, td {{ border: 1px solid #ddd; padding: 8px; text-align: center; }}
            th {{ background-color: #3498db; color: white; }}
        </style>
    </head>
    <body>
        <h1>光电效应实验报告</h1>
        <p>生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
        
        <h2>实验数据</h2>
        <table>
            <tr><th>波长 (nm)</th><th>频率 (×10¹⁴ Hz)</th><th>截止电压 (V)</th></tr>
            {"".join(f"<tr><td>{d[0]}</td><td>{d[1]}</td><td>{d[2]:.3f}</td></tr>" for d in data)}
        </table>
        
        <h2>线性拟合结果</h2>
        <img src="{plot_file}" alt="拟合曲线" style="max-width: 100%;">
        <div class="result">
            <p><strong>拟合方程:</strong> U₀ = ({slope:.3e} ± {std_err:.3e})·ν + ({intercept:.3f})</p>
            <p><strong>决定系数 R²:</strong> {r_value**2:.5f}</p>
            <p><strong>计算得到的普朗克常量:</strong> {h_calculated:.3e} J·s</p>
            <p><strong>理论普朗克常量:</strong> 6.62607015e-34 J·s</p>
            <p><strong>相对误差:</strong> {abs(h_calculated-6.62607015e-34)/6.62607015e-34*100:.2f}%</p>
        </div>
        
        <h2>结论</h2>
        <p>通过光电效应实验测量并计算得到的普朗克常量与理论值相符,验证了爱因斯坦光电效应方程的正确性。实验误差可能来源于波长测量精度、电压读数误差以及环境光干扰等因素。</p>
    </body>
    </html>
    """
    
    with open(output_file, 'w') as f:
        f.write(html)
    
    return output_file

# 使用示例
generate_experiment_report(experiment_data)

在实际项目中,我发现将实验数据处理流程封装成函数后,不仅提高了分析效率,还能确保每次实验都采用一致的分析方法。特别是在需要重复进行相似实验的课程教学中,这种自动化处理方法可以节省大量时间,让学生更专注于物理概念的理解而非繁琐的数据处理。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐