ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TwinCAT3与VS2019 ADS通信实战:从环境配置到读写订阅全攻略

TwinCAT3与VS2019 ADS通信实战:从环境配置到读写订阅全攻略 刚把 TwinCAT3 和 VS2019 之间的 ADS 通信跑通时我的第一反应是这套东西如果有一条完整链路能省下多少人的周末。工控圈里做上位机开发的兄弟应该都懂Beckhoff 的 TwinCAT3 功能强但它和 Visual Studio 绑得特别深第一次接触的人很容易在环境配置和通信细节上栽跟头。网上资料零碎官方文档偏晦涩社区提问也大多是单点解答。这篇实战记录我从 Win10 系统的环境准备开始一路写到 VS2019 里通过 ADS 和 TwinCAT3 做真实的数据交互覆盖连接、读写、订阅通知这些核心操作最后把我在实际项目中踩过的坑和排查链路完整放出来。不管你是刚接触工控通信还是已经写过几版上位机但对 ADS 的某些行为存疑这篇都值得收藏。1. 为什么是 ADS 这条路TwinCAT3 与 VS2019 的边界和配合1.1 TwinCAT3 到底是个什么“PLC”很多人第一次打开 TwinCAT3 会愣一下这界面怎么长得跟 Visual Studio 一模一样这不是错觉。TwinCAT3 的 XAEeXtended Automation Engineering开发环境本来就基于 Visual Studio Shell 构建所以安装 TwinCAT3 后你会看到熟悉的解决方案资源管理器、工具箱、错误列表这些面板。但要注意TwinCAT3 的运行时并不是普通 Windows 进程而是跑在实时内核上的。真正执行 PLC 代码、控制 EtherCAT 总线的是 Windows 底下那个独立实时环境 R0Ring 0。Windows 这边负责界面、通信、文件系统实时那边负责逻辑控制两边通过 ADS 来通信。这个架构决定了你写的 C# 上位机本质上是在 Windows 用户态里当一个 ADS 客户端向 TwinCAT3 的实时运行时发起请求。这种设计的优势很明显PLC 程序和上位机开发可以共享同一套工具链习惯变量声明在 PLC 里用 ST 语言C# 里直接用符号名去访问不需要关心地址映射。所以你在 VS2019 里装 TwinCAT 扩展也好单独用 TwinCAT3 Shell 也好最终的产品形态都是一套可调试、可监控的控制系统。1.2 ADS 在整条链路里的定位ADSAutomation Device Specification是 Beckhoff 定义的应用层协议它做的事情很简单让外部程序找到 TwinCAT 系统里的某个设备比如 PLC 运行时、NC 轴、或者自定义任务然后向这个设备的某个变量发起读或写请求。你可以把 ADS 通信看成寄快递每一个 TwinCAT 系统有一个唯一的 AMS NetId类似收件地址形如 192.168.0.10.1.1系统内部每个可访问的组件有一个端口号比如 PLC Runtime 固定是 851系统管理服务是 10000。当你指定了“NetId 端口”就等于锁定了收件人然后快递单的内容就是你想要读写的变量符号路径比如MAIN.nCounter。在整条链路里TwinCAT3 是被请求方VS2019 里运行的 C# 程序是请求方。两者可以在同一台机器上也可以跨网络。跨网段通信时需要通过 TwinCAT System Manager 里的添加路由Add Route来建立 AMS 路由表这和普通 TCP/IP 直连略有不同。后面实战部分我会重点演示。2. 环境准备Win10 上的 TwinCAT3 与 VS2019 安装陷阱2.1 版本选型与兼容性标题里说的是 Win10 平台但 Win10 版本对 TwinCAT3 的实际影响很大。官方支持的是 Win10 专业版和企业版家庭版虽然能装上但实时能力和网络配置都会受限我见过不少人在家庭版上折腾半天最后还得重装系统。工控机建议装机时就选专业版省下的时间远比系统费用值钱。TwinCAT3 的版本号一般长这样3.1.4024.xx 或 3.1.4022.xx。较新的 4024 系列才完整支持 VS2019 风格的扩展。不要在旧版本上花时间直接到 Beckhoff 官网下载最新的 TwinCAT 3.1 版本即可。安装 TwinCAT3 之前先把 VS2019 装好这样 TwinCAT 的工程模板会自动集成到 VS2019 里新建项目时你能直接看到 TwinCAT XAE Project 那一类模板。先装 TwinCAT3 再装 VS2019 也不是不行但偶尔会出现模板不注册的情况又得修复扩展没必要踩这个弯。VS2019 的版本社区版、专业版、企业版都能用。上位机 C# 项目用社区版足够了只有当你需要把 TwinCAT3 工程也挂在 VS2019 里调试时才需要考虑授权问题。对大多数场景我建议是VS2019 只做上位机程序TwinCAT3 用它自带的 TcXaeShell 打开两边互不干扰。2.2 驱动签名问题最容易卡住的坎TwinCAT3 安装完成后第一次启动会要求激活实时驱动。在 Win10 上这一步经常因为驱动签名问题失败系统提示Error: TwinCAT RT driver could not be installed。原因很简单Win10 默认强制驱动签名而 TwinCAT 的实时驱动在安装阶段没有经过签名验证。解决方法是重启用高级启动选项进入“禁用驱动程序强制签名”模式再安装驱动。具体操作设置 → 更新和安全 → 恢复 → 高级启动 → 立即重新启动然后选择“疑难解答 → 高级选项 → 启动设置 → 重启”最后按数字键 7 进入禁用驱动强制签名模式。装好驱动后再正常重启驱动保持已安装状态。还有一种更彻底的办法是进 BIOS 关闭 Secure Boot但不同主板位置不一样工控机上可以这么干个人电脑折腾容易出问题不太推荐。2.3 VS2019 侧需要装什么VS2019 这边不需要安装 TwinCAT 的完整开发环境只要你的 C# 项目能引用 ADS 库就行。最省事的方法是在 NuGet 包管理器里搜索TwinCAT.Ads安装官方包。这里有个小坑NuGet 上的包有两个大版本方向。老的Beckhoff.TwinCAT.Ads对应 .NET Framework 4.x如果你创建的是经典控制台应用或 WinForms用这个没问题。新版TwinCAT.Ads支持 .NET 6 和 .NET Core适合新项目。VS2019 默认创建的 .NET Core 3.1 控制台是可以兼容新版包的如果你在 VS2019 里用 .NET Framework 4.7.2那就找老包。所以建项目前先想清楚目标框架不要装完发现引用冲突。防火墙方面TwinCAT 安装时通常会自动添加防火墙例外但如果你的电脑装了三方安全管家这个例外可能会被吞掉。等会儿通信失败排查时这一项要重点检查。3. TwinCAT3 侧配置激活 PLC 运行时才能谈通信3.1 新建 TwinCAT XAE 项目并编写测试变量打开 TcXaeShell或者 VS2019 里的 TwinCAT 扩展新建一个 TwinCAT XAE Project。项目建好后在解决方案资源管理器里找到 PLC 那一栏右键添加一个新项选择 Standard PLC Project命名比如叫PlcDemo。进入 PLC 项目后你会看到一个MAIN程序在 MAIN 里写几个简单的测试变量。第一次做通信验证不需要搞复杂的运动控制就让 PLC 自己跑一个计数器加一个模拟温度值就行PROGRAM MAIN VAR bEnable : BOOL; bRunning : BOOL; nCounter : INT; rTemperature : REAL; sMessage : STRING(20); END_VAR IF bEnable THEN bRunning : TRUE; nCounter : nCounter 1; rTemperature : 20.5 0.1 * nCounter; sMessage : PLC Run; ELSE bRunning : FALSE; sMessage : PLC Stop; END_IF这段代码逻辑很直白上位机写bEnable为 TRUEPLC 里的计数器开始累加温度模拟值跟着变化同时返回一个字符串状态。等通信跑通了你可以在 C# 里实时刷新这些值就会看到数字往上跳。写完 PLC 代码先编译Build确认没有语法错误。PLC 程序编译成功后会生成符号表这就是 ADS 能通过符号名访问变量的基础。3.2 激活配置、进入 Run 模式、记录地址信息编译通过后回到 TwinCAT XAE 的顶层界面点击工具栏上的“Activate Configuration”按钮。这一步会把当前工程配置激活到实时环境弹窗问你“是否重启 TwinCAT System”选 Yes。激活完成后TwinCAT 进入 Run 模式PLC 程序开始周期性执行。在继续之前有两个关键信息必须拿到手第一个是 AMS NetId。在解决方案资源管理器里展开SYSTEM节点找到Router右侧能看到本机的 AMS NetId通常长这样192.168.0.10.1.1。后面 C# 连接时会用到。第二个是 PLC Runtime 的端口号。展开PLC → PlcDemo → PlcTask的“Image”之类结构不方便更直接的看法是展开SYSTEM → Online里面能看到当前激活的 PLC Runtime 端口一般就是 851。如果后面 C# 连的是旧版本 TwinCAT也可能是 852多个 PLC 项目会是 851、852 依次排下去。记住这个端口号。注意只要 TwinCAT 没有激活配置或者处于 Config ModeADS 连接虽然能建上但读取 PLC 变量会报错因为符号表不存在。这一步不是可选项是必要前提。4. VS2019 侧 ADS 通信实战从连接、读写到订阅通知4.1 创建 C# 项目并引入 TwinCAT.Ads 包打开 VS2019新建一个 .NET Core 控制台应用或 .NET Framework 控制台应用看你哪个熟悉。项目名随意比如AdsClientDemo。右键项目 → 管理 NuGet 程序包搜索TwinCAT.Ads安装版本。如果你用的是新包注意包名下面是TwinCAT.Ads而不是Beckhoff.TwinCAT.Ads命名空间都是TwinCAT.Ads。安装完成后先把目标平台的位数确认好。TwinCAT ADS 库对 x64 和 x86 都支持但如果你后面用 32 位的 AMS 路由器接口最好把平台目标设为 x64。回看 MSBuild 时会碰到“托管和本机代码混合”的提示这点在后面排查章节细说。4.2 建立 ADS 连接在 Main 方法里写一个最简单的连接测试using System; using TwinCAT.Ads; class Program { static void Main(string[] args) { using (var client new TcAdsClient()) { // 替换成你机器上的 AMS NetId 和 PLC 端口号 string amsNetId 192.168.0.10.1.1; int plcPort 851; try { client.Connect(amsNetId, plcPort); Console.WriteLine(ADS 连接成功当前连接状态: client.IsConnected); } catch (Exception ex) { Console.WriteLine(连接失败: ex.Message); } } } }这段代码第一步是Connect传入 AMS NetId 和端口。注意Connect方法不会因为地址不对就立刻抛异常很多情况下要等到真正读写数据时才报错所以仅靠这一步判断连接成功不够。我一般会在连接后立刻尝试读取一次系统时间或者读一个 PLC 变量确认链路真的通了。4.3 用符号名读写 PLC 变量连接建立后最实用的操作就是通过变量名直接读写。看下面这段// 读取 PLC 中的 INT 变量 nCounter int counter client.ReadSymbolint(MAIN.nCounter); Console.WriteLine($当前 nCounter {counter}); // 写入 BOOL 变量 bEnable client.WriteSymbol(MAIN.bEnable, true); Console.WriteLine(bEnable 已写入 TRUE); // 读取 REAL 变量 float temp client.ReadSymbolfloat(MAIN.rTemperature); Console.WriteLine($当前温度 {temp}); // 读取 STRING 变量 string msg client.ReadSymbolstring(MAIN.sMessage); Console.WriteLine($PLC 消息 {msg});ReadSymbolT是泛型版本在较新的 ADS 包中可以直接把结果转换成对应类型。老版本包里没有这个泛型重载需要用ReadSymbol返回 object 再强转object rawCounter client.ReadSymbol(MAIN.nCounter); short counter (short)rawCounter; // PLC 的 INT 对应 C# 的 short为什么这里 INT 要对应 shortPLC 的 INT 是 16 位有符号整数C# 里的 int 是 32 位。ADS 库做类型映射时不会自动把 16 位整型转成 32 位直接读成 int 会得到错误值或异常。这是新手特别容易踩的类型坑后面我会再强调。WriteSymbol的用法和读类似只要传入变量路径和值就行。写入 bool 是最常见的主令控制比如启停设备。但要注意PLC 程序的循环很块如果你写入太快PLC 可能来不及响应所以工业场景一般写入后要稍作延迟再确认状态位。4.4 高频数据用 AddNotification别只顾着轮询如果你的上位机界面每隔 50ms 读一次温度用ReadSymbol轮询确实能跑但这会给 ADS 通信和 Windows 消息循环增加不少负担。而且轮询有个致命问题数据变化的瞬间你可能没读到中间值直接被吞掉。更优雅的方案是使用 ADS 的通知Notification机制。通知机制有点像订阅公众号PLC 变量一变ADS 服务端主动推消息给客户端不用客户端反复问。实现代码也不复杂using TwinCAT.Ads; var client new TcAdsClient(); client.Connect(192.168.0.10.1.1, 851); client.AdsNotification OnAdsNotification; // 订阅 nCounter 变量变化超过 10 时通知死区为 0 uint notifyHandle client.AddNotification( MAIN.nCounter, AdsTransMode.OnChange, 100, // 循环时间 ms 0, // 死区 new NotificationConfig()); // 回调函数 private static void OnAdsNotification(object sender, AdsNotificationEventArgs e) { var value e.ReadValueshort(); Console.WriteLine($收到通知nCounter 当前值 {value}); }这里有几个关键点要记牢。第一AddNotification返回的notifyHandle必须保存下来如果它被垃圾回收通知就会悄悄停掉程序不报错就是不更新特别坑。第二回调里不能做耗时操作UI 更新需要使用Invoke或BeginInvoke切到 UI 线程。第三如果使用AdsNotificationEventArgs的ReadValueT读取数据T 要跟 PLC 实际类型严格对应不然一样会解析错误。我当时第一次用 Notification 就踩了回调风暴的问题把死区设成 0、循环时间设成 1msPLC 温度值跳一次回调疯狂触发UI 卡成 PPT。后来把循环时间设为 100ms、死区按变量小数位数设成 0.05一切正常了。高频数据用延迟 100ms 的 OnChange 模式对大多数监控界面来说足够流畅CPU 占用还能降到几乎可忽略。5. 实测高频踩坑连接失败的排查链路5.1 0x745 错误与 R0 实时环境我第一次跑 C# 程序时报的是Error 0x745一开始完全没头绪。后来查了 ADS 错误码才知道0x745 代表 TwinCAT 运行时没有进入 Run 状态。这个错误的排查链路是这样的先回到 TcXaeShell 看 TwinCAT 系统是不是在 Run 模式。只要路由器和 PLC 任务处于绿色运行图标才算正常。其次检查 Real-Time 环境是否启动如果 TwinCAT 是 Config Mode 或者根本没激活ADS 连接建立不了。最后检查 VS2019 是不是以管理员身份运行的。TwinCAT 的 ADS 路由器对权限敏感普通权限启动的客户端有时能连但无法读取实时变量。链路里最容易忽略的是第 4 个因素Windows 的“快速启动”会干扰 TwinCAT 服务启动。Win10 默认开启快速启动关机再开机后 TwinCAT 服务可能没有完全加载实时驱动。遇到莫名其妙连不上、重启 TwinCAT 就好了的情况多半是快速启动在捣鬼。工控机上建议把快速启动关掉Windows 设置 → 电源选项 → 选择电源按钮的功能 → 更改当前不可用的设置 → 取消勾选“启用快速启动”。5.2 连上了却读不到 PLC 变量连接没报错但一读MAIN.nCounter就抛异常这种最常见的原因是符号表对不上。PLC 程序每次修改、重编译、重新激活后TwinCAT 的符号文件xxx.tmc会更新但 ADS 客户端可能还缓存着旧的符号信息。解决方法是重新激活配置后再启动上位机或者在代码里使用TcAdsSymbolInfoLoader重新加载符号。另一个隐蔽的原因是你写的符号路径首字母大小写不对。ADS 符号访问在 TwinCAT3 里是区分大小写的PLC 变量是nCounter你写Ncounter一样找不到。路径里的模块名也要注意如果你把 PLC 程序块重命名过旧名字会残留在老符号表里新代码里必须用新路径。还有一种是版本错配。TwinCAT 3.1.4024 的 ADS 库如果被老包引用读STRING变量时可能报长度不对反之新库连旧 TwinCAT 也可能遇到Symbol not found。统一版本是最省心的TwinCAT 侧更新到最新NuGet 包也更新到最新不要混用。5.3 防火墙和网卡配置对 ADS 路由的影响有一次客户现场的工控机装了杀毒软件我把程序拷贝过去像被静止掉一样连接总是超时。后来检查发现杀毒软件把 TwinCAT 的防火墙例外全部干掉了ADS 的 UDP/TCP 端口被拦。ADS 通信默认使用 UDP 48898、TCP 48898 等端口不同版本略有差异。排查办法很简单先在 Windows 防火墙的高级设置里手动添加入站规则放行 TwinCAT 相关程序和端口。如果你用的是简单防火墙软件把TcXaeShell、TwinCAT Service、.exe上位机程序都加入例外。还有一个细节如果 TwinCAT3 和上位机不在同一台机器上必须在 TwinCAT System Manager 里添加远程路由右键SYSTEM → Router选择 Add Route填入目标电脑的 IP 和 AMS NetId。很多人跨网连不上不是网线问题而是根本没加路由。多网卡的时候 ADS 更容易出问题。工控机上经常同时有网卡、EtherCAT 专用网卡和普通网卡。TwinCAT 的 AMS 路由器会自动选一张网卡做为广播地址如果你上位机连到了错误的 IP 上NetId 就对不上。排查时可以 Ping 一下 IP但更加彻底的还是用 TwinCAT 自带的TcXaeShell → System → Router查看路由器绑定的网卡手动指定正确的那张。5.4 32 位/64 位目标不匹配的隐蔽问题还有一类问题只在你发布程序时爆发在 VS2019 里调试完全正常但编译成 Release 拿到现场跑一读变量程序就崩溃。这种九成是目标平台选错。ADS 库分原生和托管两部分如果你把项目平台目标设成AnyCPU在 64 位系统上会以 64 位进程运行但某些老版TwinCAT.Ads原生依赖不支持 64 位进程。我的建议是直接显式设为 x64或者如果你引用的是老包就设为 x86。不要用 AnyCPU除非你能保证所有现场机器环境一致。这类崩溃最折磨人因为异常可能不是抛在 C# 层而是原生层直接进程崩掉。排错时先在项目属性 → 生成 → 平台目标里检查一遍再决定往下查。6. 项目落地前的封装思考6.1 通信层别裸奔设计一个简单的封装实际项目里上位机不是只有一个窗口读几个变量那么简单。控制界面、报警界面、配方管理可能都要访问同一个 ADS 连接。最直接的做法是在项目里创建一个PlcConnector类把TcAdsClient封装进去对外只暴露 Read/Write/Subscribe 这三个方法。public class PlcConnector : IDisposable { private TcAdsClient _client; private readonly string _amsNetId; private readonly int _plcPort; public PlcConnector(string amsNetId, int plcPort) { _amsNetId amsNetId; _plcPort plcPort; _client new TcAdsClient(); } public void Connect() { _client.Connect(_amsNetId, _plcPort); } public T ReadT(string symbolName) { return _client.ReadSymbolT(symbolName); } public void WriteT(string symbolName, T value) { _client.WriteSymbol(symbolName, value); } public uint SubscribeT(string symbolName, ActionT onChanged) { _client.AdsNotification (s, e) { var val e.ReadValueT(); onChanged(val); }; return _client.AddNotification(symbolName, AdsTransMode.OnChange, 100, 0, new NotificationConfig()); } public void Dispose() { _client.Dispose(); } }这个封装虽然简单但它把 ADS 的细节隔离在项目其他部分之外上层界面不用关心符号名是字符串还是路径后面如果换成别的品牌 PLC替换实现类就行。真实项目里我会在这个类里加一个线程安全的SemaphoreSlim保护读写并发因为多个界面线程同时读写时TcAdsClient不是完全线程安全的有些方法内部会竞争。6.2 性能优化与断线重连ADS 通信性能上限其实很高但瓶颈往往在上层调用方式。比如循环读几十个变量的场景与其每个变量单独ReadSymbol不如用AdsSumCommand一次打包读取。这个命令把多个变量请求合并成一个报文特别适合配方导入、批量上传这种场景。高频数据尽量走AddNotification而不是轮询前面已经说过。如果对延迟要求特别严格比如轴状态监控建议用 C 的 ADS 库或者在实时环境下直接用 TwinCAT 的IPC共享内存C# 更适合做界面和业务逻辑。断线重连是现场必须处理的。TwinCAT 系统可能因为 PLC 断电重启、EtherCAT 断线、或者工程师重新激活配置而临时断开ADS 客户端连接会失效。我习惯在监控类里加一个定时任务每 500ms 检查IsConnected如果为 false就尝试重新Connect并重新订阅变量。重连成功后要重新订阅所有通知因为旧的 notification 句柄已经失效。这一步不做好PLC 重启后上位机界面会永远停在旧数据上操作工一按启动键程序还认为设备没起来后果很严重。现场还有一种情况PLC 程序更新版本后变量路径变了但上位机没有同步更新程序启动时读不到符号。这个可以在启动时调用GetSymbols()或CreateSymbolLoader()对比一遍符号表把缺失的变量名在日志里列出来比运行时一个个异常优雅得多。还有一个小建议如果你是新手调试 ADS 通信时不要一上来就写大批量变量。先跑通一个 BOOL 的写和一个 INT 的读确认方向再用循环把其他变量接进来。这样即使出问题也知道是哪一步的问题不会一头扎进代码海洋里出不来。
返回列表