关于 matlab-modernize-daq
面向 MATLAB 用户的技能,用于迁移旧版 Data Acquisition Toolbox 脚本或编写多功能采集流程。它将基于 session 的调用映射到 R2020a 的 DataAcquisition 接口,并提示回调、触发器和生命周期中的常见问题。
主要功能
- 01将 session API 调用映射到 DataAcquisition
- 02指导触发器与时钟配置
- 03涵盖回调驱动的流式采集
- 04讲解输出缓冲与自动补充
- 05检查脚本中的常见静默错误
适用场景
- 01迁移使用 daq.createSession 的脚本
- 02替换旧事件监听器和生命周期调用
- 03编写含触发器或回调的流式脚本
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-modernize-daq安装后在 Claude Code 中输入/matlab-modernize-daq确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
manifest.yamlreferences/callback-state-patterns.mdcanonical-snippets.mdsession-to-modern-mapping.mdscripts/verifyDataAcquisition.m详细介绍
将 DAQ 代码迁移到现代 DataAcquisition 接口
将基于旧版会话的 Data Acquisition Toolbox 代码(daq.createSession、addAnalogInputChannel、DataAvailable 侦听器、startBackground、queueOutputData、wait)迁移到 R2020a 引入的现代 DataAcquisition 接口(daq("ni")、addinput、ScansAvailableFcn、start、write、preload)。也涵盖如何编写新的多功能 DAQ 脚本:在认知负荷较高时,现代 API 模式很容易出错。
适用场景
- 迁移调用
daq.createSession、addAnalogInputChannel、addAnalogOutputChannel、addCounterInputChannel、addCounterOutputChannel或addDigitalChannel的现有脚本。 - 将
DataAvailable/DataRequired/ErrorOccurred侦听器替换为ScansAvailableFcn/ScansRequiredFcn/ErrorOccurredFcn。 - 替换
startBackground/startForeground/s.wait()生命周期调用。 - 将
queueOutputData替换为preload+write。 - 编写结合以下两项或更多功能的新 DAQ 脚本:外部触发、连续采集、由回调驱动的数据流、带补充数据的模拟输出,或跨回调共享状态。
- 调试类似
Undefined function 'wait' for input arguments of type 'daq.interfaces.DataAcquisition'或Unrecognized method, property, or field 'Data' for class 'matlabshared.asyncio.buffer.ElementsAvailableInfo'的错误。
不适用场景
- 不使用触发器或回调的单通道前台采集(例如,“以 10 kHz 从 ai0 采集 1 s 的电压并绘图”)。在这种规模下,现代 API 简单易用,使用此技能会徒增开销。
- 选择 DAQ 厂商或评估非 NI 硬件。此技能的证据基础是 NI。
- 对已采集的数据进行信号处理或分析(滤波、频谱分析等)。此技能仅涵盖通过 DataAcquisition 接口采集和生成数据,不涵盖后处理。
- Simulink、App Designer 或实时目标 DAQ 工作流。
- 现代化
matlab.unittest测试套件或框架式测试类(参数化 fixture、assumeTrue门控、参数数量契约测试、辅助类层次结构)。此技能中的 API 替换方法适用,但测试框架搭建工作(移除父类、重新设计 fixture、废弃针对已移除契约的测试)不在范围内。使用此技能转换测试主体中的 API 调用,然后单独处理测试框架层面的判断。
工作流
迁移基于会话的脚本或编写新的多功能 DAQ 脚本时,请按以下顺序操作。每一步完成后都要验证——此领域的大多数故障模式都不会显现。
-
盘点旧版脚本(仅适用于迁移)。 确认设备、通道类型、速率、连续采集还是有限采集、触发器/时钟连接、回调(
DataAvailable/DataRequired/ErrorOccurred)、回调间共享的状态,以及输出排队方式。 -
转换术语。 将每个会话 API 标记映射到对应的现代 API。请查阅
references/session-to-modern-mapping.md——旧版脚本中出现会话 API 标记时,务必加载此文件。常见的静默改名陷阱:NotifyWhenDataAvailableExceeds→ScansAvailableFcnCount,NumberOfScans/NumScans→ 隐式(没有对应参数),ExternalTriggerTimeout→DigitalTriggerTimeout,IsContinuous=true→start(d, "Continuous")参数。 -
构建 DataAcquisition 对象。 使用
d = daq("ni"),设置d.Rate,并通过addinput/addoutput添加通道。通过addinput/addoutput返回的通道句柄设置每个通道的属性。 -
连接触发器和时钟。 使用
references/canonical-snippets.md§1——脚本涉及addtrigger或addclock时加载此文件。参数顺序为(d, type, role, trigSrc, trigDest)——Source 是第 4 个参数,Destination 是第 5 个参数。**验证:**返回的触发器对象的Source和Destination字段应显示预期值。 -
连接回调。 使用规范代码片段 §2 — 当脚本需要
ScansAvailableFcn或ScansRequiredFcn时加载该片段。在回调内,始终通过read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix")获取数据 —evt参数的类型为matlabshared.asyncio.buffer.ElementsAvailableInfo,仅提供NumElementsAvailable,不提供Data或TimeStamps。强烈建议连接d.ErrorOccurredFcn = @(~,evt) fprintf("DAQ error: %s\n", evt.Error.message)— 如果不连接,其他回调中的异常会被静默吞掉,运行也会“成功”,但每次触发都会抛出异常。对于旧脚本没有ErrorOccurred侦听器的严格移植,应向用户说明这一权衡,而不是悄悄添加处理程序 — 让用户自行选择是否启用。 -
选择一种状态共享模式(仅限回调)。 如果回调需要在多次触发之间维护状态(运行计数器、相位累加器、锁存标志),请参阅
references/callback-state-patterns.md— 只要回调需要维护状态,就加载该文件。默认使用句柄类包装器或嵌套函数。避免使用src.UserData.X = src.UserData.X + ...修改字段;这种修改不会保留。 -
连接生命周期。 使用规范代码片段 §3 — 等待完成时加载该片段。现代对象没有
wait(d)方法。对于有限采集,优先使用start(d, "Duration", seconds(N)),然后调用read或轮询Running。对于连续采集,在回调内设置停止条件(if scansRead >= target; stop(src); end),并在调用方轮询d.Running。 -
实时执行前运行
verifyDataAcquisition。 脚本:scripts/verifyDataAcquisition.m。该脚本通过检查实时对象并搜索源文件,捕获出现频率最高的四种静默缺陷(缺少ErrorOccurredFcn、回调源代码中使用evt.Data、wait(d)令牌、UserData.X = UserData.X + ...字段修改)。如果有mcp__matlab__run_matlab_file,请通过它运行;否则,将脚本复制到用户的项目中。 -
实时验证。 运行一个较短版本(例如 1–2 s,而不是用户要求的完整时长)。检查脚本是否顺利执行到
start,回调是否至少触发一次,以及ErrorOccurredFcn是否没有打印错误。请使用mcp__matlab__run_matlab_file运行已保存的.m文件,不要使用mcp__matlab__evaluate_matlab_code— 求值字符串缓冲区以脚本模式运行,会拒绝局部函数或嵌套函数定义,而模式 5 和大多数回调主体都需要这些定义。
关键功能
| 功能 | 用途 | 工具箱 | 起始版本 |
|---|---|---|---|
daq | 创建 DataAcquisition 对象(d = daq("ni")) | Data Acquisition Toolbox | R2020a |
addinput | 添加输入通道(模拟、计数器或数字) | Data Acquisition Toolbox | R2020a |
addoutput | 添加输出通道 | Data Acquisition Toolbox | R2020a |
addtrigger | 添加触发器连接:addtrigger(d, type, role, trigSrc, trigDest) | Data Acquisition Toolbox | R2020a |
addclock | 添加时钟连接(签名类似) | Data Acquisition Toolbox | R2020a |
read | 前台采集,或在 ScansAvailableFcn 中提取数据 | Data Acquisition Toolbox | R2020a |
write | 写入输出样本(也可在 ScansRequiredFcn 中调用) | Data Acquisition Toolbox | R2020a |
preload | 在 start(d, "Continuous") 之前载入初始输出缓冲区。仅适用于输出通道。 | Data Acquisition Toolbox | R2020a |
start | 开始后台采集:start(d)、start(d, "Continuous")、start(d, "Duration", seconds(N)) | Data Acquisition Toolbox | R2020a |
stop | 停止正在运行的采集 | Data Acquisition Toolbox | R2020a |
flush | 读取前丢弃缓冲的扫描数据 | Data Acquisition Toolbox | R2020a |
daqlist | 枚举可用设备(替代 daq.getDevices) | Data Acquisition Toolbox | R2020a |
daqreset | 重置 DAQ 子系统(替代 daq.reset) | Data Acquisition Toolbox | R2020a |
DataAcquisition 的属性:Rate、Running、NumScansAcquired、NumScansOutputByHardware、ScansAvailableFcn、ScansAvailableFcnCount、ScansRequiredFcn、ScansRequiredFcnCount、ErrorOccurredFcn、DigitalTriggerTimeout、UserData。
模式
以下五种典型模式的完整内容(含已验证代码)见 references/canonical-snippets.md。下方仅列出每种模式的最简回忆片段,帮助 Agent 在高负载下唤起记忆。需要完整、可运行的形式时,请加载参考文件。
模式 1:添加外部启动触发器
trg = addtrigger(d, "Digital", "StartTrigger", "External", "Dev1/PFI0");
trg.Condition = "RisingEdge";
d.DigitalTriggerTimeout = 30;
第 4 个参数是 Source(外部信号使用 "External")。第 5 个参数是 Destination(设备端子)。两者顺序颠倒是此领域最常见的错误。触发条件("RisingEdge"、"FallingEdge")在 addtrigger 返回的触发器对象(trg.Condition)上设置,而不是在 DataAcquisition 对象上设置。不存在 d.DigitalTriggerCondition 属性——虚构此属性可以通过静态检查,但会在运行时出错。
模式 2:ScansAvailableFcn 函数体——通过 read 提取数据,绝不使用 evt.Data
d.ScansAvailableFcnCount = round(d.Rate * 0.5);
d.ScansAvailableFcn = @(src, ~) onBlock(src);
d.ErrorOccurredFcn = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);
function onBlock(src)
[data, t] = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
plot(t, data); drawnow limitrate;
end
evt 参数仅包含 NumElementsAvailable;它没有 Data 或 TimeStamps。强烈建议连接 ErrorOccurredFcn——否则,回调异常会被悄悄吞掉,运行也会“成功”,但每次触发都会抛出异常。对于严格移植没有 ErrorOccurred 监听器的旧脚本,应先询问用户,再添加监听器,不要擅自插入。
模式 3:阻塞等待采集完成——不存在 wait(d)
start(d, "Duration", seconds(10));
while d.Running
pause(0.05);
end
daq.interfaces.DataAcquisition 上不存在 wait(d)。可以轮询 d.Running,或依赖 start(d, "Duration", ...),并在回调中设置停止条件。
模式 4:带自动补充缓冲区的连续 AO 播放
d = daq("ni");
d.Rate = 1000;
addoutput(d, "Dev1", "ao0", "Voltage");
d.ScansRequiredFcnCount = d.Rate;
d.ScansRequiredFcn = @(src, ~) write(src, generateNextBlock());
preload(d, generateNextBlock());
preload(d, generateNextBlock());
start(d, "Continuous");
preload 仅适用于输出——在仅输入的配置中调用会报错。在启动前预加载两个数据块,可为首次触发 ScansRequiredFcn 留出缓冲空间。
模式 5:通过嵌套函数或句柄类共享回调间状态
function runStreamingAcquisition()
d = daq("ni");
addinput(d, "Dev1", "ai0", "Voltage");
d.Rate = 1000;
d.ScansAvailableFcnCount = 200;
scansRead = 0;
target = d.Rate * 5;
d.ScansAvailableFcn = @(src, ~) onBlock(src);
d.ErrorOccurredFcn = @(~, evt) fprintf("DAQ error: %s\n", evt.Error.message);
start(d, "Continuous");
while d.Running
pause(0.05);
end
function onBlock(src)
data = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix");
scansRead = scansRead + size(data, 1);
if scansRead >= target
stop(src);
end
end
end
嵌套函数通过引用捕获 scansRead,因此更新会在多次触发间保留。不要使用 src.UserData.X = src.UserData.X + ...——这种模式无法保留状态;每次触发时计数器都会重置,闭环控制会悄无声息地始终输出零。完整的替代方案(句柄类、完整重写 UserData)及其权衡见 references/callback-state-patterns.md。
约定
- 强烈建议在设置任何其他
*Fcn回调时,同时连接d.ErrorOccurredFcn,否则监听器会吞掉回调异常。对于严格移植缺少ErrorOccurred监听器的旧脚本,应向用户说明这种权衡,而不是悄悄添加处理程序——添加它是合理的默认做法,但忠于源代码同样有其合理性。 - 始终在
ScansAvailableFcn中通过read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix")读取数据。切勿使用evt.Data或evt.TimeStamps。 - 始终在
addtrigger后验证返回的触发器对象——确认Source和Destination符合预期。 - 切勿使用
wait(d)。应轮询d.Running,或使用start(d, "Duration", ...)。 - 切勿在回调中修改
d.UserData的字段(src.UserData.X = src.UserData.X + ...)。状态必须存储在由闭包捕获的变量、句柄类包装器中,或通过整体重写UserData结构体来更新。 - 切勿对仅输入型
DataAcquisition调用preload。它仅适用于输出。 - 优先使用
start(d, "Duration", seconds(N)),而非start(d, "NumScans", N)来执行有限时长采集。NumScans会被静默强制转换或忽略。 - 优先使用现代函数名称:
daqlist而非daq.getDevices,daqreset而非daq.reset,daq("ni")而非daq.createSession('ni')。
常见错误
| 错误 | 错误原因 | 正确做法 |
|---|---|---|
addtrigger(d, "Digital", "StartTrigger", "Dev1/PFI0", "External") | 源和目标顺序颠倒。第 4 个参数是源,第 5 个参数是目标。 | addtrigger(d, "Digital", "StartTrigger", "External", "Dev1/PFI0") |
在 ScansAvailableFcn 中调用 plot(evt.TimeStamps, evt.Data) | evt 是 ElementsAvailableInfo;其中只有 NumElementsAvailable。此调用会静默失败。 | [data, t] = read(src, src.ScansAvailableFcnCount, "OutputFormat", "Matrix") |
wait(d) | daq.interfaces.DataAcquisition 上没有此方法。调用会报错。 | while d.Running, pause(0.05); end |
src.UserData.Counter = src.UserData.Counter + n | 通过 src.UserData 修改字段不会在回调触发之间持久保留。 | 在嵌套函数中使用闭包变量,或使用句柄类包装器。请参阅 callback-state-patterns.md。 |
对仅包含输入通道的 DAQ 调用 preload(d, ...) | preload 仅适用于输出。 | 对于输入,直接调用 start(d, ...)。无需准备或预加载。 |
start(d, "NumScans", 1000) | NumScans 会被忽略——并发出警告。缓冲区大小由 preload 或 read 决定。 | start(d, "Duration", seconds(N)),或使用 preload 的大小。 |
s.IsContinuous = true; s.startBackground(); | 两者都属于 session API。现代 API 没有 IsContinuous 属性。 | start(d, "Continuous") |
addlistener(d, 'DataAvailable', @cb) | 监听器事件已重命名为对象上的 *Fcn 属性。 | d.ScansAvailableFcn = @cb |
s.queueOutputData(block) | 属于 session API。现代 API 使用 preload(初始数据)和 write(补充数据)。 | 在 start 前调用 preload(d, block);在 ScansRequiredFcn 中调用 write(d, block)。 |
参考资料
references/session-to-modern-mapping.md— 列出所有常见误用的旧版函数和属性及其完整重命名对照表。只要旧脚本中出现 session API 标识,就应加载此文件。references/canonical-snippets.md— 提供上述五种模式的精简、独立且可运行示例。编写包含多项功能的脚本并需要经过验证的参考示例时,请加载此文件。references/callback-state-patterns.md— 介绍四种状态共享方式(UserData / 闭包 / 句柄类 / 嵌套函数)、各自适用场景,以及错误选择导致的静默失败模式。任何回调需要跨触发共享状态时,请加载此文件。scripts/verifyDataAcquisition.m— 在实际执行前,对DataAcquisition对象及其源脚本进行静态检查。返回结构化报告,标出出现频率最高的四类静默问题。
版权所有 2026 The MathWorks, Inc.