You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cfg_anlg_edge_ref_trig()触发后nidaqmx丢失实时数据的问题

问题

使用Python的nidaqmx包读取NI 6356采集卡模拟通道数据时,常规无触发配置下可正常实时获取电压读数,但调用cfg_anlg_edge_ref_trig()设置触发后,nidaqmx会等待整个测量完成才返回数据,无法实现实时数据流,不符合预期。切换代码中trigger参数的True/False可复现问题,最小复现代码如下:

import nidaqmx as daq
import numpy as np
import matplotlib.pyplot as plt

from datetime import datetime
from nidaqmx.stream_readers import AnalogMultiChannelReader

def set_ai_measurement(number_samples, ai_channels, clock_frequency, trigger=False, trigger_channel='Dev1/ai0', pretrigger_samples=0, trigger_threshold=1):
    """ Configures the counter for a finite measurement.

    Args:
        ai_voltage_range (float): expected max voltages value encountered during the measurement.
        clock_frequency (int): rate of the measurement's clock.
        pretrigger_samples (int, optional): number of samples recorded before the trigger.
        trigger_threshold (int, optional): threshold for the detection of the trigger, in volts.
    """
    current_time = datetime.now().strftime("%m%d%Y_%H%M%S")
    task = daq.Task(new_task_name='task_{}'.format(current_time))
    reader = AnalogMultiChannelReader(task.in_stream)

    for chan in ai_channels:
        task.ai_channels.add_ai_voltage_chan(
            chan,
            max_val=10,
            min_val=-10)

    if trigger:
        if pretrigger_samples < 0:
            pretrigger_samples = 0
        if trigger_threshold < 0:
            trigger_threshold = 1
        if pretrigger_samples == 0:
            pretrigger_samples = 2 # Minimum value allowed
        task.triggers.reference_trigger.cfg_anlg_edge_ref_trig(
            trigger_channel,
            pretrigger_samples,
            trigger_slope=daq.constants.Slope.RISING,
            trigger_level=trigger_threshold)
        
    task.timing.cfg_samp_clk_timing(
        clock_frequency,
        sample_mode=daq.constants.AcquisitionType.FINITE,
        samps_per_chan=number_samples)
    task.start()

    return task, reader

def read_daq(number_samples, number_ai_channels, reader):
        """ Reads the current values of the channels.

        Args:
            number_samples (int): number of samples to read.

        Returns:
            np.array: array containing the datas.
        """
        _stop_event = False
        number_measurement = 0
        raw_data = np.empty((number_ai_channels, number_samples))
        while not _stop_event:
            number_measurement += 1
            current_time = datetime.now()
            if number_measurement >= number_samples and number_samples > 0:
                break
            channels_data = np.empty((number_ai_channels, 1))
            channels_data.fill(np.nan)
            try:
                reader.read_many_sample(
                    channels_data,
                    number_of_samples_per_channel=1)
                print(channels_data)
            except:
                pass
            for i in range(number_ai_channels):
                raw_data[i, 0] = channels_data[i]
            raw_data = np.roll(raw_data, -1, axis=1)

        plt.figure()
        for i in range(number_ai_channels):
            plt.plot(raw_data[i])
        plt.show()
        return 1

number_samples = 100
ai_channels = ['Dev1/ai0', 'Dev1/ai1']
clock_frequency = 20
trigger = True
trigger_channel = 'Dev1/ai0'
pretrigger_samples = 0
trigger_threshold = 4

task, reader = set_ai_measurement(number_samples, ai_channels, clock_frequency,
                   trigger, trigger_channel, pretrigger_samples, trigger_threshold)
read_daq(number_samples, len(ai_channels), reader)

分析与解决建议

你的实现存在两处关键疏漏,导致触发模式下无法实时读取数据:

1. 有限采样模式的触发逻辑限制

当配置参考触发+有限采样时,NI采集卡会先缓存预触发样本,触发事件发生后继续采集剩余样本,直到完成number_samples总数才会将全部数据一次性提交给主机。这是硬件触发在有限采样模式下的默认行为,并非代码bug,但与实时读取需求不匹配。

2. 读取逻辑的低效与错误

  • read_daq中每次尝试读取1个样本,但触发模式下,采集卡未完成全部采样前不会返回单样本数据,导致循环阻塞直到整个采集完成。
  • raw_data的滚动逻辑存在错误,每次只更新第一列后滚动,最终仅能保留最后几次样本,无法正确记录完整数据流。

针对硬件触发+实时读取的需求,可按以下方案调整:

方案1:改用连续采样模式配合触发

将采样模式改为AcquisitionType.CONTINUOUS,触发后采集卡会持续输出数据,支持实时读取:

  • 修改cfg_samp_clk_timing中的sample_mode为daq.constants.AcquisitionType.CONTINUOUS,并设置合适的samps_per_chan(建议设为时钟频率的1-2倍,作为缓存区大小)。
  • 在read_daq中,循环读取固定数量的样本(比如每次读取10个),直到达到目标总样本数后停止任务。

方案2:配置触发后的实时读取回调

利用nidaqmx的回调机制,当采集卡有新数据可用时自动触发读取函数,无需主动循环等待:

def callback(task_handle, every_n_samples_event_type, number_of_samples, callback_data):
    reader = callback_data
    data = np.empty((len(ai_channels), number_of_samples))
    reader.read_many_sample(data, number_of_samples_per_channel=number_of_samples)
    print(data)
    return 0

# 在set_ai_measurement函数中,task.start()之前添加回调注册
task.register_every_n_samples_acquired_into_buffer_event(10, callback, reader)

方案3:优化有限采样模式下的读取逻辑

若必须使用有限采样,可将总样本数拆分为预触发样本+触发后样本,配置触发后,先读取预触发缓存,触发事件发生后再实时读取后续样本,但这种方式需精确控制缓存区,实现复杂度较高。

不建议自行实现软件峰值检测,NI硬件触发的精度和可靠性远高于软件实现,优先调整硬件触发的配置逻辑即可满足需求。

内容的提问来源于stack exchange,提问作者Florentin Fabre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 15:53:13