摘要

压电陶瓷电容应使用LCR表或阻抗分析仪,在明确的等效模型、测试频率、交流激励、直流偏置、温度和机械边界下测量。对于远离谐振区的小信号测试,可先选择Cp-D并联模型,完成开路和短路补偿后进行频率扫描,同时保存电容Cp和损耗因数D。

只记录一个电容数字通常不够。压电陶瓷具有介电损耗和机电耦合,测得的等效电容会随频率、激励幅值、直流偏置、温度、预紧和安装状态变化。靠近谐振与反谐振区域时,简单电容模型可能失去清晰物理意义,不能把该数值直接代入驱动电流计算。

本文给出压电陶瓷电容测试流程和可运行Python代码。代码根据仪器导出的电导G与电纳B计算Cp、D和小信号电流分量。自检数据由理想并联RC公式构造,只验证算术和文件流程,不代表任何压电陶瓷或压电执行器的实测性能。

1. 压电陶瓷电容测量的直接结论

压电陶瓷不是理想电容器。工程上所说的电容通常是仪器在指定频率和等效电路模型下拟合得到的参数。因此,完整结果至少应写成“Cp-D并联模型、测试频率、交流电压、直流偏置、温度、机械状态和补偿方式”,而不是只写多少uF或nF。

必须记录的项目 为什么重要
等效模型 Cp-D与Cs-D不是同一个参数定义,不能混写
测试频率 介电响应和机电耦合都会使结果随频率变化
交流激励 压电陶瓷具有非线性,小信号与大信号结果不能直接互换
直流偏置 偏置可能改变介电状态,仪器和夹具还需具备偏置能力
温度 介电常数与损耗可能随温度变化
机械状态 自由、预紧、夹持和装配后的机电边界不同
补偿方式 夹具、引线和接触寄生量会进入测量结果

2. Cp-D并联模型与计算原理

远离明显谐振区时,可把小信号等效导纳写成电导G与电纳B之和。对电容性样品,B应为正值。并联电容Cp和损耗因数D可按以下ASCII公式计算。

Y = G + j*B
B = 2*pi*f*Cp
Cp = B / (2*pi*f)
D = G / B
I_reactive_rms = V_rms * B
I_loss_rms = V_rms * G

这里f是测试频率,V_rms是仪器施加到样品端的交流有效值。D表示该并联模型下有损分量与无功分量的比值。若仪器导出的是阻抗Z、串联电容Cs或相位角,应先按照仪器说明书确认符号约定和等效模型,不能直接套用上式。

3. LCR表测压电陶瓷的系统架构

在这里插入图片描述
压电陶瓷电容测试架构示意。夹具补偿和机械边界应与测量结果一起记录。

3.1 为什么优先使用四端对夹具

四端对测量可降低引线阻抗、接触阻抗和杂散耦合对宽频阻抗测量的影响,但前提是仪器、线缆和夹具按规定连接。普通两线连接在低阻抗或较高频率下可能引入明显误差。

3.2 为什么要做开路和短路补偿

开路补偿主要表征夹具的并联寄生量,短路补偿主要表征串联残余阻抗。补偿状态、夹具位置、线缆姿态或接触方式改变后,应重新评估补偿是否有效。

4. 压电陶瓷电容测试流程

在这里插入图片描述
压电陶瓷电容测试流程。发布数据前应保留原始导纳或阻抗及完整条件。

  1. 断开高压驱动,按器件和驱动器要求完成受控放电,并用合适仪表确认端电压。
  2. 记录样品编号、结构状态、安装方式、预紧条件和环境温度。
  3. 按仪器说明完成夹具开路与短路补偿,必要时增加负载补偿或参考件核查。
  4. 选择Cp-D或其他明确模型,设置交流激励、频率范围、点数和积分时间。
  5. 从远离已知谐振区的频点开始,再进行宽频扫描以识别阻抗和相位变化。
  6. 保存原始G、B或阻抗幅值与相位,不只保存仪器计算后的电容。
  7. 重新连接样品并重复测量,以识别接触、夹具和装夹变化。
  8. 报告中写明数据状态。没有真实仪器与样品记录时,只能标注为理论或示例。

5. 软件、硬件与测试条件

类别 最低记录要求
被测对象 样品编号、器件类型、极化方向、安装和预紧状态
测量仪器 LCR表或阻抗分析仪型号、固件、量程和校准状态
夹具线缆 夹具型号、连接方式、线缆长度、补偿日期和补偿类型
扫频条件 频率起止、点数、交流有效值、直流偏置和积分时间
环境条件 温度、湿度、热稳定时间和振动环境
重复策略 样品数、重复连接次数、扫频方向和异常值规则
本文代码 Windows 11 64位,Python 3.12,仅使用标准库
代码输入 CSV中的频率、电导、电纳、交流电压和温度

若目标是压电驱动器选型,还需要在实际电压波形、频率、温度和机械负载下测量驱动电流。小信号LCR电容只能作为输入之一,不能替代大信号动态电流验证。

6. Python批量分析代码

下面代码是可运行的离线分析工具。它不控制LCR表,不执行夹具补偿,也不判断数据是否处于谐振区。输入数据必须已经由测试人员完成符号、单位、模型和补偿检查。

from __future__ import annotations

import argparse
import csv
import json
import math
from pathlib import Path

REQUIRED_COLUMNS = {
    "sample_id",
    "frequency_hz",
    "conductance_s",
    "susceptance_s",
    "voltage_vrms",
    "temperature_c",
}

def finite_float(value: str, field: str, line_number: int) -> float:
    try:
        number = float(value)
    except ValueError as exc:
        raise ValueError(f"invalid {field} at line {line_number}") from exc
    if not math.isfinite(number):
        raise ValueError(f"non-finite {field} at line {line_number}")
    return number

def positive_float(value: str, field: str, line_number: int) -> float:
    number = finite_float(value, field, line_number)
    if number <= 0:
        raise ValueError(f"{field} must be positive at line {line_number}")
    return number

def analyze_row(raw: dict[str, str], line_number: int) -> dict[str, object]:
    sample_id = raw["sample_id"].strip()
    if not sample_id:
        raise ValueError(f"empty sample_id at line {line_number}")

    frequency_hz = positive_float(raw["frequency_hz"], "frequency_hz", line_number)
    conductance_s = finite_float(raw["conductance_s"], "conductance_s", line_number)
    susceptance_s = positive_float(
        raw["susceptance_s"], "susceptance_s", line_number
    )
    voltage_vrms = positive_float(raw["voltage_vrms"], "voltage_vrms", line_number)
    temperature_c = finite_float(raw["temperature_c"], "temperature_c", line_number)

    if conductance_s < 0:
        raise ValueError(f"conductance_s must not be negative at line {line_number}")

    capacitance_f = susceptance_s / (2.0 * math.pi * frequency_hz)
    dissipation_factor = conductance_s / susceptance_s

    return {
        "sample_id": sample_id,
        "frequency_hz": frequency_hz,
        "conductance_s": conductance_s,
        "susceptance_s": susceptance_s,
        "voltage_vrms": voltage_vrms,
        "temperature_c": temperature_c,
        "capacitance_f": capacitance_f,
        "capacitance_uf": capacitance_f * 1e6,
        "dissipation_factor": dissipation_factor,
        "reactive_current_arms": voltage_vrms * susceptance_s,
        "loss_current_arms": voltage_vrms * conductance_s,
    }

def read_and_analyze(path: Path) -> list[dict[str, object]]:
    results: list[dict[str, object]] = []
    with path.open("r", encoding="utf-8-sig", newline="") as handle:
        reader = csv.DictReader(handle)
        if reader.fieldnames is None:
            raise ValueError("CSV has no header")
        missing = REQUIRED_COLUMNS.difference(reader.fieldnames)
        if missing:
            raise ValueError(f"missing columns: {sorted(missing)}")
        for line_number, raw in enumerate(reader, start=2):
            results.append(analyze_row(raw, line_number))
    if not results:
        raise ValueError("CSV contains no data rows")
    return results

def create_self_test(path: Path) -> None:
    # Formula-generated values for arithmetic verification only.
    capacitance_ref_f = 1.2e-6
    dissipation_ref = 0.02
    voltage_vrms = 0.5
    frequencies_hz = [100.0, 1000.0, 10000.0, 20000.0]

    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.writer(handle)
        writer.writerow([
            "sample_id",
            "frequency_hz",
            "conductance_s",
            "susceptance_s",
            "voltage_vrms",
            "temperature_c",
        ])
        for frequency_hz in frequencies_hz:
            susceptance_s = 2.0 * math.pi * frequency_hz * capacitance_ref_f
            conductance_s = dissipation_ref * susceptance_s
            writer.writerow([
                "TEACHING_RC",
                format(frequency_hz, ".12g"),
                format(conductance_s, ".12g"),
                format(susceptance_s, ".12g"),
                format(voltage_vrms, ".12g"),
                "25",
            ])

def verify_self_test(results: list[dict[str, object]]) -> None:
    if len(results) != 4:
        raise AssertionError(f"expected 4 rows, got {len(results)}")
    for row in results:
        if not math.isclose(
            float(row["capacitance_f"]), 1.2e-6, rel_tol=1e-9, abs_tol=1e-15
        ):
            raise AssertionError(f"capacitance self-test failed: {row}")
        if not math.isclose(
            float(row["dissipation_factor"]), 0.02, rel_tol=1e-9, abs_tol=1e-15
        ):
            raise AssertionError(f"dissipation self-test failed: {row}")

def write_csv(path: Path, results: list[dict[str, object]]) -> None:
    with path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=list(results[0].keys()))
        writer.writeheader()
        writer.writerows(results)

def main() -> None:
    parser = argparse.ArgumentParser(
        description="Analyze compensated Cp-D admittance exports."
    )
    source = parser.add_mutually_exclusive_group(required=True)
    source.add_argument("--input", type=Path)
    source.add_argument("--self-test", action="store_true")
    parser.add_argument("--output-dir", type=Path, default=Path("."))
    args = parser.parse_args()

    args.output_dir.mkdir(parents=True, exist_ok=True)
    if args.self_test:
        input_path = args.output_dir / "piezo_capacitance_self_test.csv"
        create_self_test(input_path)
    else:
        input_path = args.input

    results = read_and_analyze(input_path)
    if args.self_test:
        verify_self_test(results)

    write_csv(args.output_dir / "piezo_capacitance_results.csv", results)
    summary = {
        "data_status": "formula-generated self-test" if args.self_test else "user input",
        "row_count": len(results),
        "self_test_status": "passed" if args.self_test else "not_applicable",
        "capacitance_uf_min": min(float(row["capacitance_uf"]) for row in results),
        "capacitance_uf_max": max(float(row["capacitance_uf"]) for row in results),
        "dissipation_factor_min": min(
            float(row["dissipation_factor"]) for row in results
        ),
        "dissipation_factor_max": max(
            float(row["dissipation_factor"]) for row in results
        ),
        "boundary": (
            "The code does not perform fixture compensation, resonance detection, "
            "large-signal characterization, uncertainty evaluation, or safety control."
        ),
    }
    with (args.output_dir / "piezo_capacitance_summary.json").open(
        "w", encoding="utf-8"
    ) as handle:
        json.dump(summary, handle, ensure_ascii=False, indent=2)
    print(json.dumps(summary, ensure_ascii=False, indent=2))

if __name__ == "__main__":
    main()

6.1 运行自检

python piezo_capacitance_analyzer.py --self-test --output-dir self_test_output

6.2 分析真实仪器导出数据

python piezo_capacitance_analyzer.py --input measured_admittance.csv --output-dir result

真实CSV必须使用S和Hz等SI单位。若仪器导出mS、uS、kHz或负电纳,应先按说明书统一单位和符号。

sample_id,frequency_hz,conductance_s,susceptance_s,voltage_vrms,temperature_c
PZ_SAMPLE_01,1000,0.00012,0.0069,0.5,25

7. 可验证数据与测试方法

本文没有连接真实LCR表和压电器件,因此不发布产品电容、损耗或驱动电流实测值。下表仅记录代码自检的理论构造条件。
在这里插入图片描述

项目 自检设置 数据状态 验证方法
并联电容 1.2e-6 F 理论构造值 由B除以2乘pi乘f反算
损耗因数 0.02 理论构造值 由G除以B反算
频率点 100、1000、10000、20000 Hz 教学点 四个频点均应恢复相同参考值
交流电压 0.5 Vrms 教学输入 只用于计算电流分量
真实产品性能 没有样品、夹具或仪器数据 未测试 不能由自检结果推断

发布真实测试数据时,应附原始导纳或阻抗文件、夹具补偿记录、仪器型号与校准状态。应先绘制Cp和D随频率变化的曲线,再标记谐振附近异常区。若需要批次统计,还必须增加样品数量、抽样规则、重复连接和测量不确定度。

8. 常见错误与工程边界

8.1 只在一个频率测一次

单点值不能显示频率依赖,也无法发现已接近谐振。至少应先做探索性扫频,再选择有依据的报告频点。

8.2 混淆Cp与Cs

并联电容Cp和串联电容Cs来自不同等效模型。离开低损耗、远离谐振等条件后,两者差异可能增大,报告必须写明模型。

8.3 补偿后改变线缆和夹具

移动线缆、改变夹具间距或更换转接件会改变寄生参数。补偿配置与真实测量连接不一致时,应重新补偿。

8.4 用小信号电容直接预测大信号电流

压电陶瓷的介电非线性、迟滞和损耗会随电场与频率变化。小信号Cp只能用于初步估算,驱动器验证仍需真实波形和电流测量。

8.5 忽略放电和偏置安全

压电器件具有储能特性。未放电就切换到LCR表,可能损坏仪器并造成人员风险。直流偏置路径也必须满足仪器额定要求。

9. 常见问题

在这里插入图片描述

9.1 压电陶瓷电容用万用表能测吗

万用表电容档可用于粗略筛查,但通常不能完整设置频率、激励、模型和夹具补偿。工程报告应使用可记录测试条件的LCR表或阻抗分析仪。

9.2 LCR表应该选择Cp-D还是Cs-D

取决于频率、损耗和用途。远离谐振的小信号高阻抗样品常从Cp-D开始,但必须遵循器件规格与仪器指南,并在报告中固定模型。

9.3 压电陶瓷电容测试频率应该选多少

没有适用于所有器件的固定频率。应依据规格书、实际驱动频段和探索性扫频结果选择,并避开使简单模型失真的谐振区域。

9.4 测量电压越高越接近实际使用吗

不一定。普通LCR表通常做小信号表征,大信号测试需要额定高压源、限流、隔离和同步电流测量,不能仅提高LCR表测试电压。

9.5 为什么同一压电执行器测出的电容不同

常见原因包括频率、交流幅值、直流偏置、温度、预紧、安装、夹具补偿和等效模型不同。应先对齐条件再比较。

9.6 谐振附近的电容值可以用于驱动器选型吗

不能直接使用。谐振附近机电支路占主导,应分析完整阻抗和相位,并按实际工作波形测量驱动电流与稳定性。

9.7 损耗因数D越小越好吗

D需要结合频率、温度、模型和应用评价。单一低值不能证明器件效率、寿命或动态性能,也不能跨不同测试条件直接排名。

9.8 本文Python代码能代替LCR表吗

不能。代码只处理已补偿的导纳数据,不产生测量激励,也不完成夹具补偿、校准、谐振判断、大信号测试或安全控制。

10. 结论

压电陶瓷电容测试的关键不是得到一个孤立数字,而是让电容值与Cp-D模型、测试频率、交流激励、直流偏置、温度、夹具补偿和机械状态绑定。先扫频识别谐振区域,再选择报告频点,结果才具有可比较性。
在这里插入图片描述

对于压电执行器驱动电流和带宽设计,小信号LCR电容是重要输入,但不是全部依据。大信号应用仍需在目标波形、负载和温度条件下测量电压、电流与器件状态,并保留原始数据和不确定度说明。

参考资料

  1. IEEE Std 176-1987,IEEE Standard on Piezoelectricity,Institute of Electrical and Electronics Engineers。https://ieeexplore.ieee.org/document/79638
  2. Keysight Technologies,Impedance Measurement Handbook,Application Note 5950-3000。https://www.keysight.com/us/en/assets/7018-06840/application-notes/5950-3000.pdf
  3. Keysight Technologies,E4980A Precision LCR Meter,产品与技术资料。https://www.keysight.com/us/en/product/E4980A/precision-lcr-meter-20-hz-2-mhz.html
  4. National Institute of Standards and Technology,NIST Technical Note 1297,Guidelines for Evaluating and Expressing the Uncertainty of NIST Measurement Results。https://www.nist.gov/pml/nist-technical-note-1297
  5. Python Software Foundation,csv and math modules,Python 3.12 documentation。https://docs.python.org/3.12/library/csv.html

说明:标准、仪器手册和应用笔记可能更新。正式测试应核对当前仪器型号、固件版本、夹具说明和器件规格书。

Logo

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

更多推荐