尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

倍福PLC上位机开发入门:用ADS通讯读取TwinCAT数组数据

发布时间:2026/9/26 12:43:24

资讯中心
01
ARTICLE

倍福PLC上位机开发入门:用ADS通讯读取TwinCAT数组数据

倍福PLC上位机开发入门:用ADS通讯读取TwinCAT数组数据
干这行的都知道倍福Beckhoff的PLC在非标自动化、高端设备制造里出场率很高而上位机跟TwinCAT交换数据最直接的一条路就是走ADSAutomation Device Specification通讯。很多第一次接触倍福的工程师看完官方文档都是一头雾水一堆DLL、一堆端口号、还有AMS NetId这种概念根本不知道从哪下手。这篇文章要聊的Sample01是Beckhoff官方示例里最简单的一个入门Demo核心就一件事从.NET上位机程序里把PLC侧的一组数组数据读出来显示到界面上。Sample01本身代码量不大但它把ADS通讯的标准流程给你串明白了怎么建立连接、怎么通过符号名或地址读取数据、读出来的类型怎么和PLC对齐。搞懂了它后面玩Notification订阅、写PLC数据、调参数都不是难事。适合刚接触倍福上位机开发的工程师也适合那些已经在用OPC UA或者Modbus、想换个更底层更灵活的通讯方式的同学参考。1. 项目概述与ADS方案选型1.1 为什么选择ADS通讯方案先说说为什么我们最终选ADS而不是用OPC UA或者直接用Modbus TCP。ADS是Beckhoff自己定义的通讯协议运行在TCP/IP之上但它比OPC UA更“贴肉”。举个例子你用OPC UA读PLC数组需要先在服务器端配好节点映射客户端再用UA Client去订阅链路长中间多了一层抽象。ADS则直接通过TwinCAT的Router机制访问变量表只要你知道变量名字或者内存地址就能直接读几乎没有额外开销。在工程现场时间就是成本。ADS通讯的开发周期很短尤其你只是把数据从PLC拽回上位机的时候它比OPC UA那套配置流程要快得多。但ADS也不是没有短板它对网络环境的依赖比较强跨网段访问时需要配置TwinCAT Router的静态路由表这也是后面要重点讲的坑之一。Sample01选择的通讯方案正是基于标准ADS连接。它不涉及复杂的安全认证、不依赖第三方中间件直接在.NET进程里引用官方的TwinCAT.Ads.dll就能完成从连接到读取的全部过程。对于做设备调试、数据采集这类周期性很强的工控场景这种轻量方案反而最可靠。1.2 Sample01示例的核心价值Sample01作为官方示例价值不在于它有多少行代码而在于它展示了ADS通讯最标准的代码骨架。我接触过的不少项目包括后来做的设备数据采集系统、MES对接中间件核心流程基本就是从这个骨架上长出来的。你可以把Sample01理解成一个“模板工程”。它告诉你三件事第一TcAdsClient这个核心类怎么创建和释放第二连接PLC时端口号该填什么比如TwinCAT 3的PLC运行系统默认是851第三读取数组时API层面应该调用ReadAny还是ReadSymbol参数该怎么给。把这些搞明白了你再看官方文档里的其他复杂示例比如Notification、读写任意内存区域会发现只是在这个基础上加了更多API调用而已。这也是我为什么建议新手不要一上来就啃那本几百页的ADS手册先把Sample01跑通再顺着它去扩展。2. 环境准备与项目搭建2.1 .NET Framework版本怎么选官方示例最早是基于.NET Framework 3.5写的后来也提供了.NET 4.x的版本。这里有个实际工程问题很多工控电脑还是Windows 7或Windows 10老环境装.NET Framework 3.5偶尔会报错比如热词里那个错误代码0x80d03805所以我的建议是直接用.NET Framework 4.7.2或4.8。为什么推荐4.x而不是3.5一方面4.8在Windows 10/11上是可选功能开启一下就行而且与老代码基本兼容另一方面TwinCAT.Ads这个库本身从早期版本到新版迭代很多新版本对.NET 4.x支持更好API也更完整比如后来增加的ReadSymbol系列方法。如果你遇到的是在Win10上装.NET Framework 3.5失败的情况不用死磕直接在Visual Studio里把目标框架切到4.7.2然后重新编译就行。老项目升到4.x一般改动很小Sample01这种示例更是不用改逻辑只换框架版本就能跑起来。2.2 TwinCAT ADS库的引入方式引入ADS库有两种方式我实际项目里都用过。第一种如果你电脑上装了TwinCAT 3XAE开发环境那么这个DLL就在安装目录下具体路径一般是C:\TwinCAT\AdsApi\TwinCAT.Ads.dll直接在Visual Studio里添加引用就能用。这种方式的好处是版本一定和你的TwinCAT开发环境匹配不会出现连不上PLC的问题。第二种通过NuGet包管理器安装TwinCAT.Ads包。这种方式更适合搞独立上位机开发的场景因为你可能只需要ADS通讯功能不想为了一个DLL把整个TwinCAT安装包都装到上位机电脑上。NuGet会帮你处理依赖版本控制更灵活。我后来做交付给客户的采集程序就是打包了这个NuGet包客户电脑只要装了对应的.NET运行时就行不需要装TwinCAT全家桶。注意一点ADS库分32位和64位如果你的上位机跑的是64位程序就去选用x64的平台目标如果PLC侧有老旧的第三方驱动或老版本TwinCAT有时候32位反而更稳。我踩过坑用64位连接不上换成x86编译立马通了这个后面在问题排查部分细说。2.3 一个最小可运行项目的搭建流程为了让小白也顺利跑通我把步骤写到可以直接照抄的级别打开Visual Studio新建一个控制台应用程序.NET Framework4.7.2。项目名称随意比如AdsSample01。右键“引用”如果走NuGet就搜索TwinCAT.Ads如果是本机装了TwinCAT则直接添加DLL引用。确认项目平台目标如果PLC和上位机都在同一操作系统下建议直接选x64省事。在Main方法里写基本连接代码先连上再读数据。编译运行。如果一切正常你会在控制台窗口里看到从PLC读到的数组内容从0到9依次打出来前提是你PLC侧的程序里确实定义了对应变量。这个工程模板我后来一直保存着每次做新项目就直接复制一份改改特别省时间。3. 核心代码解析读取PLC数组3.1 建立ADS连接ADS连接的核心类是TcAdsClient。很多第一次接触的人会把它类比成Socket其实不太准确它内部封装了TwinCAT Router的握手过程。最基本的使用就两行TcAdsClient adsClient new TcAdsClient(); adsClient.Connect(851);这里的851是端口号对应TwinCAT 3的PLC运行系统。如果你用的是TwinCAT 2或者TwinCAT 3里有多个PLC任务比如分配了别的端口这个数字会不一样。判断方法很简单在TwinCAT的系统管理器里看“PLC”项的属性里面就有AMS Port号。还有一点很多人困惑本地连接和远程连接传的参数不一样。本地连接也就是上位机软件和PLC跑在同一台工控机里直接传端口号就行远程连接则要多传一个AMS NetId比如adsClient.Connect(192.168.1.20.1.1, 851);这个AMS NetId是PLC的实时内核在TwinCAT Router里注册的标识前四段通常是PLC网卡的IP地址后两段是设备标识号。记不住没关系在TwinCAT系统管理器右上角能看到当前系统的AMS NetId直接复制。3.2 读取数组的核心实现连接建立之后读取PLC数组最推荐的写法和最稳妥的写法略有差异。先说推荐写法用ReadSymbol按变量名读取static void Main(string[] args) { TcAdsClient adsClient new TcAdsClient(); try { adsClient.Connect(851); // 读取PLC中的数组假设PLC侧声明的变量是 arrData: ARRAY[0..9] OF DINT int[] plcArray (int[])adsClient.ReadSymbol(MAIN.arrData, typeof(int[])); for (int i 0; i plcArray.Length; i) { Console.WriteLine($arrData[{i}] {plcArray[i]}); } } catch (Exception ex) { Console.WriteLine(读取失败: ex.Message); } finally { adsClient.Dispose(); } }ReadSymbol方法的核心在于第二个参数typeof(int[])它告诉ADS库你要把PLC侧的数据当成什么类型来反序列化。PLC侧如果是DINT数组对应到.NET就是int[]如果是INT数组对应short[]。另一种更偏底层的方式是用ReadAnyint[] plcArray (int[])adsClient.ReadAny( 0x000000F0, // IndexGroup通常是数据区域 myOffset, // IndexOffset偏移地址 typeof(int[]), new int[] { 10 } // 告诉ADS要读10个元素 );这种方式需要你事先知道变量在PLC内存中的地址维护成本高一旦PLC里的变量顺序调整了地址就对不上了。所以我个人建议除非你是在做老TwinCAT 2项目或者必须走地址访问否则优先用ReadSymbol可读性好、不容易错。还有第三种方式是ReadArray这是比较新的API它的好处是直接返回强类型数组省掉一次拆箱操作int[] plcArray adsClient.ReadArrayint(MAIN.arrData, 0, 10);这个泛型版本的性能比ReadSymbol好一些因为少了object装箱拆箱的开销在频繁读取的场合会体现出来。Sample01里的逻辑其实就涵盖了这三种思路的雏形你在官方源码里可能看到的是其中一种但我在工程里都试过我自己现在的代码习惯是用ReadArray。3.3 数据映射与类型转换ADS通讯里类型映射是最容易踩坑的地方。因为PLC侧的DINT不是C#里的INTC#的int是32位PLC的INT其实是16位如果不注意读出来的数据会莫名其妙翻倍或截断。下面这个对应关系表建议直接收藏PLC数据类型存储大小.NET对应类型备注BOOL1字节bool或byte读BOOL数组时用bool[]BYTE1字节byte很少单独用INT2字节short注意不是intDINT4字节int最常用REAL4字节float注意不是doubleLREAL8字节double和C#的double对应STRING80字节默认string长度按PLC声明为准ARRAY OF DINT4*N字节int[]数组类型要匹配元素类型我遇到过不止一次PLC里定义的是ARRAY[0..9] OF INT16位上位机这里按int[]去读结果数组里每个数看起来都变成了原来的两倍还加了一堆乱七八糟的值。这类问题的根源都是类型宽度不匹配C#的int是32位而PLC的INT是16位ADS库按你传入的类型去切字节流宽度不匹配数据自然错乱。所以读数组前一定先回PLC程序里看一眼变量类型。搞定类型再提性能。数组元素越多读取时长线性增加但如果只是几十个元素的数组完全不用操心。真正的坑在于数组跨越内存块边界时的读法这个放到问题排查部分讲。4. 常见问题与排查技巧实录4.1 连接失败怎么排查连接失败是Sample01最常见的问题。通常分两种情况一种是连本机都失败提示找不到目标路由或超时另一种是远程连接失败上位机连工控机上的PLC失败。本地连接失败九成是端口号不对或者TwinCAT系统没跑起来。先在工控机上打开TwinCAT系统管理器确认PLC的AMS端口是多少再看看系统状态是不是Run。很多新手建了PLC项目只是激活了配置但没点“运行”那PLC Runtime根本不在ADS自然连不上。远程连接失败先ping一下IP通不通然后看TwinCAT Router配置。远程访问需要在开发机上把PLC的AMS NetId添加到静态路由表里这一步常被遗漏。添加方式在Windows托盘区域找到TwinCAT Router图标右键打开Router配置添加一个静态路由填PLC的AMS NetId和IP。添加完要重启Router进程生效。另外还有个老生常谈Windows防火墙。TwinCAT的ADS服务默认监听48898端口广播跟路由相关还会用到很多高位端口你可以在防火墙里放通TwinCAT相关进程。如果你在客户现场遇到“上午能连通、下午连不上”的情况很多时候就是防火墙策略或者笔记本用了无线网卡换了IP导致。4.2 数据读不对类型不匹配与数组越界读出来的数据不对优先检查类型映射表。本章前面那张表照着核对一遍。比如PLC的REAL数组你如果用double[]去读表面看数值挺像那么回事实际上小数点后的精度和大小都可能是错的因为32位浮点的位模式被硬解释成了64位浮点。还有数组越界的问题。ReadSymbol在编译期无法检查数组长度它在运行时是按变量的真实长度去读的。但如果你是用ReadAny或者ReadArray指定了长度务必确保你给的长度不超过PLC数组声明的大小。一旦超出ADS会返回一个错误状态码在C#里会抛出异常。真正麻烦的是长度不够比如数组有20个元素你只读了10个程序不报错但数据不完整容易引发业务逻辑上难以定位的Bug。我实际写过一段很蠢的代码把PLC一个声明长度为100的DINT数组循环读了1000次每次都从偏移加结果大部分数据都是错的。因为跨过数组末尾之后ADS会给你读相邻变量的内存垃圾。后来学聪明了直接改读整个数组一次再在NET侧处理。所以数组读取要及时摆脱“循环偏移”思维优先整块读。4.3 性能优化与工程落地建议Sample01在简化场景下怎么读都行但实际生产环境里如果你要在大屏、看板或者数据采集服务里高频读取PLC数据就必须注意下面几点。第一连接对象复用。不要每次读周期都new一个TcAdsClient再Connect连接握手很耗费时间。正确做法是在服务启动时建立连接整个生命周期复用同一个客户端实例用完后统一Dispose。我测试过多线程场景下频繁Connect/Dispose的损耗大概是你读取本身的几十倍纯属自找麻烦。第二高频读取时改用ADS Notification。如果你只需要在PLC数据变化时获得通知而不是自己掐着每秒50毫秒去轮询那就要用AddDeviceNotification。这个属于Sample01的进阶玩法但一旦你的采集频率超过100HzNotification方案的优势就非常明显因为它在数据没变化时不消耗通讯带宽。第三用数组批量读代替逐个读。如果PLC里有一百个变量不要循环调用ReadSymbol一百次这是大多数人在代码评审阶段都会犯的错。而是应该把相关变量打包到一个结构体里Structured类型或者用数组方式一次读回。ADS是实时的工业协议通讯的物理开销比数据处理开销大得多批量操作比逐个操作能快上一个数量级。第四注意.NET任务的线程亲和性。ADS客户端不是线程安全的如果多个线程同时读写同一个TcAdsClient实例会出现随机异常。我用SemaphoreSlim或者lock做串行化宁可牺牲一点并行度也不能让连接状态不可控。最后的落地建议写一个单独的ADS通讯服务类把连接、读取、类型转换、断开封装起来再暴露事件或者回调给上层业务。不要像Sample01那样把通讯逻辑全写在Main函数里——那是教学演示不是工程结构。我接手过好几个出问题的项目最后定位完都是因为通讯代码和业务逻辑揉在一起改一个参数要动一堆地方。结尾的几句大白话我自己从跑通Sample01到做正式项目有一个很深的感触ADS通讯的官方示例代码短但文档里藏着很多“弦外之音”没人带着梳理一遍自己很容易卡在那些看似简单的细节上。比如AMS NetId的配置、端口号的选择、类型映射表每一样都是“看起来容易、实际坑人”的典型。这篇笔记基本把我日常写倍福上位机时高频用到的东西都过了一遍希望你也少走几步弯路。如果你现在正打算读PLC里的数组数据拿Sample01这个示例先跑通把连接方式、读法、类型映射都验证一遍再去往业务层扩展这是最快也最稳的路子。后续熟练了可以继续研究Notification怎么做、怎么把结构体整块读写、怎么把这套ADS通讯封装成稳定的服务组件。这些我都挨个趟过后面有机会再拆开讲讲。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。