Loading...
时易うさぎのBlog
闲聊工作经验

PI PZT触发功能解析、SDK用法

1. 背景

在高精度PZT扫描系统中,经常需要实现这样一种功能:

PZT按照固定速度运动,每移动固定距离触发一次相机采集,从而获得等间隔空间采样数据。

例如:

  • PZT扫描范围:100 μm

  • 触发步长:0.076 μm

  • 相机帧率:150 FPS

理想情况下,希望得到:

也就是说:

空间采样间隔应该由PZT位移决定,而不是由时间决定。


2. 传统触发方案的问题

在项目初期,实现PZT扫描触发通常有几种方式。

2.1 软件循环触发

最简单的方案是由上位机软件控制:

这种方案实现简单,但存在明显问题。

通信延迟

整个链路:

会受到:

  • Windows线程调度

  • 程序执行时间

  • 通信周期

  • SDK响应时间

影响。

因此很难保证触发位置严格对应实际空间位置。


高速扫描能力有限

当扫描速度提高后:

  • PZT移动速度增加

  • 触发间隔缩短

  • 软件判断周期不足

容易出现:

  • 触发位置偏移

  • 数据间距不均匀

  • 丢触发

软件循环更适合低速定位场景,而不适合作为高精度PZT扫描触发方案。


2.2 外部硬件时间触发

为了改善软件触发的稳定性,项目中曾采用过一种硬件时间触发方案。

由硬件工程师设计了一块独立触发板:

触发板按照固定时间间隔输出脉冲。

每 6.67ms 输出一次Trigger

≈150 FPS

例如:

相比软件循环:

优势:

  • 不依赖Windows系统调度

  • 触发周期更加稳定

  • 延迟更低

  • 相机采集更加可靠

实际效果明显优于软件轮询。


但是这种方案仍然存在一个本质问题:

它控制的是时间间隔,而不是空间间隔。

例如:

PZT速度 = 10 μm/s

Trigger周期 = 10ms

理论采样间隔:

10 × 0.01 = 0.1 μm

但是实际运行过程中,如果PZT受到:

  • 环境振动

  • 机械共振

  • 负载变化

  • 微小速度波动

那么:

最终可能出现:

对于普通成像影响不大,但对于:

  • 纳米级位移扫描

  • 表面轮廓测量

  • 共聚焦扫描

会导致:

  • 点云比例失真

  • 局部拉伸/压缩

  • 测量误差增加


2.3 PI控制器位置触发

相比时间触发,PI PZT控制器提供了硬件级位置触发功能。

触发逻辑:

此时触发条件由:

时间

变成:

空间位置

例如设置:

Trigger Step = 0.076 μm

控制器自动:

0

0.076

0.152

0.228

...

每经过固定距离输出一次Trigger。

这种方式:

  • 不依赖PC实时性

  • 不受Windows调度影响

  • 不受通信周期影响

  • 触发点与PZT位置同步

  • 对环境振动更加不敏感

因此对于高精度扫描系统:

最理想的触发方式不是固定时间触发,而是固定空间距离触发。


3. PI GCS触发功能简介

PI的GCS(General Command Set)中,Trigger Output主要由两个接口完成。

CTO:Configure Trigger Output

CTO用于配置触发规则。

可以设置:

  • Trigger模式

  • 参考轴

  • 触发步长

  • 起始位置

  • 结束位置

简单理解:

CTO负责告诉控制器什么时候产生Trigger。


TRO:Trigger Output

TRO用于开启或者关闭触发输出。

简单理解:

TRO负责打开Trigger输出开关。

完整流程:


4. PI位置触发模式

本文使用:

Position Distance

即:

每经过固定距离输出一次Trigger。

例如:

设置:

Trigger Step = 0.076 μm

控制器产生:

0

0.076

0.152

0.228

...

每到达一个位置点:

输出一次触发。


5. 触发参数说明

主要参数:

参数

作用

TriggerMode

触发模式

Axis

触发参考轴

TriggerStep

触发距离

StartThreshold

开始触发位置

StopThreshold

结束触发位置

TRO

开启/关闭输出

例如:

扫描:

Start = 10 μm

End = 20 μm

Step = 0.1 μm

控制器产生:

10.1

10.2

...

20.0

共100次触发。


6. SDK调用流程

6.1 设置触发模式

PI.GCS2.CTO(
    handle,
    new int[] { Digital_OUT_1 },
    new int[] { P_TriggerMode },
    new double[] { PositionDistance },
    1);

6.2 设置触发轴

PI.GCS2.CTOString(
    handle,
    new int[] { Digital_OUT_1 },
    new int[] { P_Axis },
    "1",
    1);

6.3 设置触发间隔

例如:

TriggerStep = 0.076;

表示:

每移动:

0.076 μm

输出一次触发。


6.4 设置触发范围

例如:

Start = 0

End = 100

对应:

StartThreshold = 0

StopThreshold = 100

控制器只在该范围内输出Trigger。


6.5 开启触发

PI.GCS2.TRO(
    handle,
    new int[]{Digital_OUT_1},
    new int[]{1},
    1);

7. 实际项目代码

以下代码用于配置PI PZT位置触发。

功能:

  • 设置触发步长

  • 设置扫描范围

  • 开启/关闭Trigger Output

public static int Digital_OUT_1 = 1;
public static int Digital_OUT_2 = 2;
public static int Digital_OUT_3 = 3;

public enum PztTriggerMode
{
    PositionDistance = 0,
    OnTarget = 2,
    MinMaxThreshold = 3,
    GeneratorTrigger = 4,
    InMotion = 6
};

public enum PztTriggerParameter
{
    P_TriggerStep = 1,
    P_Axis = 2,
    P_TriggerMode = 3,
    P_MinThreshold = 5,
    P_MaxThreshold = 6,
    P_StartThreshold = 8,
    P_StopThreshold = 9
};

/// <summary>
/// 配置触发输出
/// </summary>
/// <param name="step">步长</param>
/// <param name="start">起始位置</param>
/// <param name="end">结束位置</param>
/// <param name="isenable">是否启用</param>
public void SetTriOutConfigure(
    float step,
    double start,
    double end,
    bool isenable)
{
    if (!isenable)
    {
        // 设置Trigger模式关闭
        double[] CTOparamsoff =
        {
            (double)PztTriggerMode.InMotion
        };
        int result =
        PI.GCS2.CTO(
            handle,
            new int[] { Digital_OUT_1 },
            new int[] {
                (int)PztTriggerParameter.P_TriggerMode
            },
            CTOparamsoff,
            1);
        if(result != 1)
        {
            LogHelper.Debug(
            "PI_CTO error : PositionDistance");
            return;
        }
        // 设置触发轴
        result =
        PI.GCS2.CTOString(
            handle,
            new int[] {Digital_OUT_1},
            new int[]{
                (int)PztTriggerParameter.P_Axis
            },
            axes,
            1);
        if(result != 1)
        {
            LogHelper.Debug(
            "PI_CTOString error : Axis");
            return;
        }
        // 关闭Trigger输出
        result =
        PI.GCS2.TRO(
            handle,
            new int[]{Digital_OUT_1},
            new int[]{0},
            1);
        if(result != 1)
        {
            LogHelper.Debug(
            "PI_TRO error : disable");
        }
        return;
    }
    // ==========================
    // Enable Trigger
    // ==========================
    // 1. Position Distance模式
    double[] CTOparams =
    {
        (double)PztTriggerMode.PositionDistance
    };
    int isok =
    PI.GCS2.CTO(
        handle,
        new int[]{Digital_OUT_1},
        new int[]{
            (int)PztTriggerParameter.P_TriggerMode
        },
        CTOparams,
        1);
    if(isok != 1)
    {
        LogHelper.Debug(
        "PI_CTO error : PositionDistance");

        return;
    }
    // 2. 设置触发轴

    isok =
    PI.GCS2.CTOString(
        handle,
        new int[]{Digital_OUT_1},
        new int[]{
            (int)PztTriggerParameter.P_Axis
        },
        "1",
        1);
    if(isok != 1)
    {
        LogHelper.Debug(
        "PI_CTOString error : Axis");

        return;
    }
    // 3. 设置Trigger Step
    isok =
    PI.GCS2.CTO(
        handle,
        new int[]{Digital_OUT_1},
        new int[]{
            (int)PztTriggerParameter.P_TriggerStep
        },
        new double[]{step},
        1);
    if(isok != 1)
    {
        LogHelper.Debug(
        "PI_CTO error : TriggerStep");

        return;
    }
    // 4. 设置触发范围
    isok =
    PI.GCS2.CTO(
        handle,
        new int[]{Digital_OUT_1},
        new int[]{
            (int)PztTriggerParameter.P_StartThreshold
        },
        new double[]{start},
        1);
    if(isok != 1)
        return;
    isok =
    PI.GCS2.CTO(
        handle,
        new int[]{Digital_OUT_1},
        new int[]{
            (int)PztTriggerParameter.P_StopThreshold
        },
        new double[]{end},
        1);
    if(isok != 1)
        return;
    // 5. 开启Trigger Output
    isok =
    PI.GCS2.TRO(
        handle,
        new int[]{Digital_OUT_1},
        new int[]{1},
        1);
    if(isok != 1)
    {
        LogHelper.Debug(
        "PI_TRO error : enable");
    }
}

8. 扫描时序建议

推荐流程:

注意:

不要在PZT加速阶段开始采集。

更推荐:

也就是说在触发范围前后各留一段距离。

这样可以避免扫描前后数据间距异常。


9. 速度与相机帧率匹配

Trigger步长决定空间采样率。

计算公式:

PZT速度 = TriggerStep × CameraFPS

例如:

步长:

0.076 μm

相机:

150 FPS

理论速度:

0.076 × 150 = 11.4 μm/s

实际工程中需要留出余量。

推荐:

9~10 μm/s

避免:

  • 相机丢帧

  • Trigger堆积

  • PZT速度波动造成采样误差

实际相机的极限帧率应当根据测试情况来决定。


10. 总结

PI PZT硬件触发核心只有两个接口:

接口

作用

CTO

配置触发规则

TRO

开启/关闭触发

对于高精度扫描:

相比:

  • 软件轮询触发

  • 固定时间硬件触发

位置触发能够保证:

  • 更高同步精度

  • 更稳定空间采样间隔

  • 更低系统延迟

适用于:

  • 共聚焦扫描

  • 白光干涉

  • 表面轮廓测量

  • PZT纳米级位移扫描


注:

本文中的CTO参数编号、Trigger模式枚举等内容与具体PI控制器型号有关,实际开发时需要参考对应控制器手册。

分享到

评论