摘要
压电执行器位移线性度测试,是把多个驱动输入点与工作端实测位移进行拟合,再用最大拟合残差或其他明确口径描述偏离程度。测试不能只测两个端点,也不能把迟滞、蠕变、重复性或传感器非线性全部混在一个“线性度”数字中。
推荐做法是固定预紧、负载、温度、驱动速率和测量基准,采集完整升程和降程数据,先分别分析单向曲线,再说明是否使用独立参考直线、端点拟合或最小二乘拟合。本文 Python 程序为可运行的离线教学示例,示例数据不代表任何产品实测指标。
图1 驱动输入、压电执行器、位移传感器和数据采集组成完整测试链路。
1. 线性度到底描述什么
| 概念 | 定义 | 与线性度的关系 |
|---|---|---|
| 位移线性度 | 实测输入输出曲线相对规定参考直线的偏离 | 必须说明参考直线和归一化分母 |
| 迟滞 | 同一输入点在升程和降程的输出差异 | 升降程混合拟合可能把迟滞带入残差 |
| 蠕变 | 输入保持不变时位移随时间变化 | 停留时间不同会改变线性度结果 |
| 重复性 | 相同程序重复执行时结果的离散程度 | 重复性差会放大拟合残差的不确定度 |
| 传感器非线性 | 测量链本身偏离真实位移的程度 | 需通过校准或独立参考评估 |
同一组位移数据使用不同参考直线,线性度结果可能不同。
2. 参考直线如何选择
常见参考方法包括端点直线、独立基准直线和最小二乘拟合直线。端点直线连接输入范围两端,便于工程快速比较;独立基准直线需要外部定义或校准依据;最小二乘直线使残差平方和最小,但不一定代表设备规格采用的口径。
- **端点法:**适合快速检查整体偏离,但端点误差会影响整条参考线。
- **最小二乘法:**适合分析曲线趋势,应同时保留斜率、截距和最大残差。
- **升降程分开拟合:**有助于把线性偏离与迟滞方向差分开。
- **满量程归一化:**分母可以是指令满量程、实测位移跨度或规格规定值,三者不能混用。
3. 测试架构与安全边界
线性度结果必须绑定工作端测量、参考直线和归一化方法。
4. 多点测试流程
- 确认驱动输入定义、位移测量基准、量程和目标工作区间。
- 固定执行器预紧、机械负载、安装方向、温度和控制模式。
- 选择覆盖全范围的多个输入点,点数和间距应在报告中说明。
- 按固定速率执行升程和降程,每个点使用一致的稳定窗口。
- 同步记录输入、工作端位移、环境温度和必要的参考测量。
- 分别对升程、降程或合并数据拟合参考直线,保存斜率、截距和残差。
- 改变一个变量复测,例如负载、速度、预紧或温度,观察线性度口径的变化。
从输入点规划、升降程采样到参考直线和残差统计的完整流程。
5. 软件、硬件与测试条件
| 类别 | 应记录内容 |
|---|---|
| 压电执行器 | 型号和版本、驱动方式、预紧、机械负载、安装边界和温度 |
| 输入通道 | 电压或命令范围、分辨率、更新率、波形和输出校准 |
| 位移测量 | 传感器类型、工作端位置、分辨率、采样率、带宽和校准状态 |
| 采集条件 | 点数、稳定窗口、升降速率、循环次数、滤波和时间同步 |
| 软件 | Windows 11 64位,Python 3.12.13,仅使用csv、json、math和statistics标准库 |
| 统计口径 | 参考直线方法、最大残差、均方根残差、归一化分母和异常值规则 |
6. Python多点线性度分析代码
CSV列为input_value、displacement_um和direction。direction取up或down。程序分别进行最小二乘直线拟合,输出斜率、截距、最大绝对残差和均方根残差。input_value是归一化教学输入,不是产品电压规格。
python linearity.py --self-test python linearity.py displacement_data.csvfrom__future__importannotationsimportargparseimportcsvimportjsonimportmathimportstatisticsfromcollectionsimportdefaultdictfrompathlibimportPathdefnumber(value:str,field:str,line:int)->float:try:result=float(value)except(TypeError,ValueError)asexc:raiseValueError(f"invalid{field}at line{line}")fromexcifnotmath.isfinite(result):raiseValueError(f"non-finite{field}at line{line}")returnresultdefload(path:Path):withpath.open("r",encoding="utf-8-sig",newline="")ashandle:reader=csv.DictReader(handle)required={"input_value","displacement_um","direction"}missing=required-set(reader.fieldnamesor[])ifmissing:raiseValueError(f"missing columns:{sorted(missing)}")rows=[]forline,rawinenumerate(reader,start=2):direction=raw["direction"].strip().lower()ifdirectionnotin{"up","down"}:raiseValueError(f"direction must be up or down at line{line}")rows.append({"input_value":number(raw["input_value"],"input_value",line),"displacement_um":number(raw["displacement_um"],"displacement_um",line),"direction":direction})iflen(rows)<2:raiseValueError("at least two rows are required")returnrowsdeffit(rows):iflen(rows)<2:raiseValueError("at least two points are required for a fit")xs=[row["input_value"]forrowinrows]ys=[row["displacement_um"]forrowinrows]x_bar=statistics.fmean(xs)y_bar=statistics.fmean(ys)denominator=sum((x-x_bar)**2forxinxs)ifdenominator==0:raiseValueError("input values must not all be equal")slope=sum((x-x_bar)*(y-y_bar)forx,yinzip(xs,ys))/denominator intercept=y_bar-slope*x_bar residuals=[y-(slope*x+intercept)forx,yinzip(xs,ys)]return{"points":len(rows),"slope_um_per_input":slope,"intercept_um":intercept,"max_abs_residual_um":max(abs(value)forvalueinresiduals),"rms_residual_um":math.sqrt(sum(value*valueforvalueinresiduals)/len(residuals))}defanalyze(rows):groups=defaultdict(list)forrowinrows:groups[row["direction"]].append(row)return{direction:fit(items)fordirection,itemsinsorted(groups.items())}defself_test():rows=[]fordirection,offsetin(("up",0.10),("down",-0.10)):forx,errorin((0.0,0.0),(1.0,0.05),(2.0,-0.05),(3.0,0.0)):rows.append({"input_value":x,"displacement_um":2.0*x+offset+error,"direction":direction})result=analyze(rows)assertresult["up"]["points"]==4assertresult["down"]["points"]==4assertresult["up"]["max_abs_residual_um"]>0assertabs(result["up"]["intercept_um"]-0.10)<0.03print("SELF_TEST_OK")print(json.dumps(result,ensure_ascii=False,indent=2))defmain():parser=argparse.ArgumentParser(description="Analyze piezo displacement linearity")parser.add_argument("csv_path",nargs="?",type=Path)parser.add_argument("--self-test",action="store_true")args=parser.parse_args()ifargs.self_test:self_test()elifargs.csv_pathisNone:parser.error("csv_path is required")else:print(json.dumps(analyze(load(args.csv_path)),ensure_ascii=False,indent=2))if__name__=="__main__":main()程序只做最小二乘拟合和残差统计,不执行运动控制、传感器校准或迟滞补偿。正式报告应保留原始CSV、拟合方法和不确定度说明。
7. 教学结果与曲线判读
自检为升程和降程各4个输入点。升程在拟合直线附近加入正负0.05 um教学残差并叠加0.10 um偏置,降程叠加负0.10 um偏置。结果用于验证分组、拟合和残差输出,不代表产品性能。
| 方向 | 点数 | 拟合斜率 | 最大绝对残差 | 数据属性 |
|---|---|---|---|---|
| 升程 | 4 | 约2.00 um/输入单位 | 约0.04 um | 教学计算 |
| 降程 | 4 | 约2.00 um/输入单位 | 约0.04 um | 教学计算 |
曲线应同时显示实测点、拟合直线、残差和方向信息。
8. 常见失效原因和排查顺序
- 先验证输入通道和位移传感器的零点、比例和采样同步。
- 确认升降程速率、停留时间和温度一致,避免蠕变进入拟合残差。
- 检查预紧、偏载和夹具刚度,避免机械边界变化造成曲线弯折。
- 比较不同参考直线方法,确认结果变化来自定义而不是数据错误。
- 保留异常点和剔除理由,不得为了缩小残差而随意删点。
- 将线性度、迟滞、重复性和漂移分别报告,避免一个指标承担多个物理含义。
9. 常见问题
9.1 线性度越小越好吗
在同一测试定义和归一化分母下,残差越小通常表示偏离参考直线越小,但不能脱离测量不确定度和适用范围比较。
9.2 端点法和最小二乘法哪个正确
两者都是常见方法,关键是与规格或报告口径一致。不同方法的结果不能直接混用。
9.3 为什么升程和降程要分开拟合
分开拟合可以观察方向相关偏差,避免把迟滞和线性偏离混成一个残差。
9.4 线性度测试需要多少个点
点数应覆盖工作范围并能发现曲线弯折。具体点数要结合规格、行程和测量能力,不能用一个固定数字适用于所有平台。
9.5 线性度和准确度是一回事吗
不是。线性度描述相对参考直线的形状偏离,准确度还包含零点、比例、参考真值和其他系统误差。
9.6 传感器噪声会影响线性度吗
会。噪声会增大残差离散,尤其在位移变化较小或采样点较少时,应先评估噪声底。
9.7 教学数据中的0.04 um可以作为产品指标吗
不能。它是构造数据的拟合结果,不含具体型号、仪器、环境、负载和校准记录。
9.8 是否可以用线性度结果直接生成补偿表
不能直接生成。补偿还要考虑迟滞、蠕变、温度、动态带宽、传感器噪声和控制器限幅,并经过独立验证。
10. 结论
压电执行器位移线性度测试的核心,是在固定机械和环境条件下进行多点采样,明确参考直线和归一化分母,并分别观察升程、降程和残差。线性度结果不能替代迟滞、蠕变、重复性或准确度测试。
本文代码、表格和曲线均为可复现教学示例,不代表鸿芯微控或其他具体产品的实测性能。正式发布线性度指标前,必须补充型号、输入范围、测量仪器、采样率、负载、温度、循环次数、拟合方法和原始记录。
参考资料
- ISO,ISO 230-2:2014,Test code for machine tools - Part 2: Determination of accuracy and repeatability of positioning。ISO官方页面
- ISO,ISO 5725-1:1994,Accuracy of measurement methods and results - Part 1。ISO官方页面
- JCGM,JCGM 100:2008,Evaluation of measurement data - Guide to the expression of uncertainty in measurement。BIPM官方页面
- IEEE,ANSI/IEEE Std 176-1987,IEEE Standard on Piezoelectricity。IEEE官方来源
- Python Software Foundation,statistics模块文档,Python 3.12。Python官方文档
- Python Software Foundation,csv模块文档,Python 3.12。Python官方文档