
1. 工控通信里绕不开的FINS协议到底是个什么东西搞工业自动化的朋友尤其是做产线数据采集、设备联网、上位机开发的大概率都碰过欧姆龙PLC。碰过欧姆龙PLC的人十有八九被FINS协议折腾过。我第一次接触FINS大概是好几年前当时要做一个产线数据采集的小系统PLC用的是欧姆龙系列上位机要实时读取几百个寄存器地址的数据。一开始想用现成的OPC服务器结果授权费用高得离谱而且延迟也不理想。后来被逼着自己啃FINS协议从报文格式到命令码从TCP到UDP踩了一圈坑之后才算真正跑通。这篇文章就把我对FINS协议的理解和实操经验完整地梳理一遍从协议原理到代码实现从常见问题到排查技巧尽量讲透。FINS全称是Factory Interface Network Service翻译过来就是工厂接口网络服务。它是欧姆龙为其PLC产品设计的一套应用层通信协议。你可以把它理解成欧姆龙设备之间、以及上位机与欧姆龙设备之间对话的“语言”。只要双方都说FINS不管底层走的是串口、以太网还是其他总线都能互相读写数据。这个设计其实挺聪明的把应用层和传输层解耦了上层命令格式统一底层换传输介质不影响上层逻辑。那FINS能做什么简单说它能让你读写PLC的内存区域比如CIO区、工作区WR、保持继电器HR、数据内存DM、扩展数据内存EM等。还能操作一些辅助区域比如定时器当前值、计数器当前值、状态标志位等。基本上你在CX-Programmer里能看到的内存地址FINS都能通过命令去读写。除此之外FINS还支持一些控制命令比如强制置位/复位、运行/停止PLC、读取PLC型号和状态信息等。适合谁来学我觉得三类人最需要第一类是做上位机SCADA或者MES系统开发的需要跟欧姆龙PLC做数据交互第二类是搞设备联网和数据采集的要把产线PLC的数据汇总到数据库或者云平台第三类是做PLC程序开发的工程师想了解底层通信机制方便调试和优化。当然如果你只是偶尔用一下组态软件那可能不需要深入了解FINS组态软件底层已经帮你封装好了。但如果你想做定制化开发或者遇到组态软件搞不定的场景FINS就是绕不过去的坎。2. FINS协议的架构设计与核心思路拆解2.1 为什么FINS要分成命令帧和响应帧FINS协议的核心交互模式非常简单上位机发一条命令帧PLC回一条响应帧。命令帧里包含目标地址、命令码、以及具体的数据参数。响应帧里包含执行结果码和返回的数据。这种一问一答的模式在工控领域非常常见好处是实现简单、逻辑清晰坏处是效率相对较低不适合大批量高频次的数据传输。但欧姆龙在设计FINS的时候做了一个很关键的决策把协议分成两层。上层是FINS命令层定义了统一的命令码和数据格式下层是传输层可以是串口、可以是TCP、可以是UDP甚至可以是其他网络协议。这个分层设计的好处是不管你用什么物理介质上层的命令格式完全一样。你学会了FINS命令的构造方法换一种传输方式只需要改底层的收发逻辑上层的报文内容不用动。我个人的理解是这种设计思路跟HTTP协议有点像。HTTP不管你是走以太网还是走WiFi请求和响应的格式是一样的。FINS也是一样命令码和数据格式是固定的传输方式可以灵活替换。这就大大降低了学习成本也提高了协议的适应性。2.2 FINS的两种主要传输方式TCP和UDP怎么选FINS over Ethernet有两种传输方式TCP和UDP。这是很多人纠结的地方到底选哪个我一开始也纠结过后来实际用下来发现两者各有适用场景。TCP方式的优点是可靠数据不会丢顺序不会乱。缺点是开销大每次通信需要建立连接、维护连接而且TCP的确认重传机制在实时性要求高的场景下反而会成为负担。UDP方式的优点是快没有连接建立和维护的开销发出去就不管了。缺点是可能丢包需要应用层自己做超时重传。那实际怎么选我的经验是这样的如果你做的是数据采集对实时性要求不是特别高但要求数据准确完整那就用TCP。比如每隔几百毫秒读一次温度、压力这些数据TCP完全够用而且不用担心丢包。如果你做的是实时控制比如运动控制、高速触发对延迟极其敏感那就用UDP。但用UDP的时候应用层一定要做超时重传和序号校验否则丢一包数据可能导致控制逻辑出错。还有一个细节欧姆龙PLC对TCP连接数是有限制的。不同型号的PLC支持的TCP连接数不一样有的支持十几个有的只支持几个。如果你用TCP方式上位机数量多了可能会连不上。UDP没有连接数的概念所以多上位机场景下UDP反而更省心。这个坑我踩过当时一个产线上有五六台上位机同时采集用TCP怎么都连不上换成UDP立马就好了。2.3 FINS的地址体系为什么有这么多内存区域FINS协议里最让人头疼的可能就是地址体系了。欧姆龙PLC的内存区域划分得很细CIO区、WR区、HR区、DM区、EM区每个区域还有不同的位和字寻址方式。初学者看到这些很容易懵。我当初也是花了不少时间才理清楚。简单来说CIO区是核心输入输出区主要用来映射物理IO和内部继电器。WR区是工作继电器区掉电不保持适合做中间变量。HR区是保持继电器区掉电保持适合做需要记忆的状态。DM区是数据内存区只能按字寻址适合存参数和数值。EM区是扩展数据内存区通常用于大容量数据存储可以分多个Bank。为什么欧姆龙要设计这么多区域其实是为了满足不同场景的需求。有的数据需要掉电保持有的不需要有的数据需要按位操作有的只需要按字读写。不同的区域有不同的特性用对了区域程序效率和可维护性都会好很多。比如你做一个配方系统配方参数肯定要掉电保持那就放DM区或者HR区。如果你做一个中间计算变量掉电丢了也无所谓那就放WR区。在FINS命令里读写这些区域需要指定区域代码和地址。区域代码是一个两位的十六进制数比如CIO区是0xB0WR区是0xB1HR区是0xB2DM区是0x82EM区是0x98到0x9A不同Bank不同代码。地址是十六进制的比如DM100就是0x0064。这些细节后面实操部分会详细讲。3. FINS报文结构深度解析与实操要点3.1 FINS命令帧的完整结构拆解要玩转FINS必须把报文结构搞清楚。FINS over TCP的报文结构分为两部分FINS/TCP头 FINS命令帧。FINS over UDP的报文结构更简单只有FINS命令帧没有额外的TCP头。先看FINS命令帧的结构。它由以下几个部分组成字段长度字节说明ICF1信息控制字段通常0x80RSV1保留字段固定0x00GCT1网关计数通常0x02DNA1目标网络地址通常0x00DA11目标节点地址DA21目标单元地址通常0x00SNA1源网络地址通常0x00SA11源节点地址SA21源单元地址通常0x00SID1服务ID用于匹配请求和响应MRC1主命令码SRC1子命令码数据可变命令参数这12个字节是FINS命令帧的固定头部后面跟着具体的数据。ICF字段通常填0x80表示需要响应。RSV固定0x00。GCT表示经过的网关数通常填0x02。DNA和DA1、DA2组成目标地址SNA和SA1、SA2组成源地址。SID是一个序号用来匹配请求和响应你可以自己维护一个递增的计数器。MRC和SRC是命令码决定了这条命令要做什么。比如读内存区的命令码是0x01 0x01写内存区的命令码是0x01 0x02。不同的命令码后面跟的数据格式不一样。这个后面会详细讲。3.2 FINS/TCP头的特殊处理如果你用TCP方式FINS命令帧前面还要加一个FINS/TCP头。这个头有16个字节结构如下字段长度字节说明Magic4固定为FINS的ASCII码Length4后面数据的长度Command4命令类型ErrorCode4错误码ClientNode4客户端节点地址仅部分命令FINS/TCP头里的Command字段有几个关键值0x00000000表示客户端节点地址申请0x00000001表示FINS帧发送0x00000002表示FINS帧接收。当你建立TCP连接后第一步通常是发送一个节点地址申请命令让PLC给你分配一个节点地址。这个命令的格式比较特殊后面实操部分会详细演示。这里有个坑要注意FINS/TCP头的Length字段是后面所有数据的长度包括FINS命令帧的长度。这个长度是字节数不是字数。我当初就是因为把长度算错了导致PLC一直不响应排查了好久才发现是长度字段填错了。3.3 读写命令的数据格式详解读内存区的命令码是0x01 0x01。命令数据部分包含以下内容字段长度字节说明区域代码1指定要读的内存区域起始地址2十六进制低字节在前位偏移1按位读取时使用按字读取时填0x00读取数量2要读取的字数或位数低字节在前写内存区的命令码是0x01 0x02。命令数据部分包含字段长度字节说明区域代码1指定要写的内存区域起始地址2十六进制低字节在前位偏移1按位写入时使用按字写入时填0x00写入数量2要写入的字数或位数低字节在前写入数据可变要写入的数据低字节在前注意地址和数据的字节序都是低字节在前这是FINS协议的一个特点。比如你要读DM100开始的10个字起始地址是100十六进制是0x0064低字节在前就是0x64 0x00。读取数量是10十六进制是0x000A低字节在前就是0x0A 0x00。这个字节序问题很容易搞错我当初就因为字节序搞反了读出来的数据全是乱的。响应帧的格式相对简单。固定头部之后跟着一个两字节的结果码。结果码0x0000表示成功其他值表示各种错误。结果码后面就是返回的数据。读命令的响应数据就是读到的内容写命令的响应没有额外数据。4. 从零实现FINS通信的完整实操过程4.1 环境准备与PLC参数配置在开始写代码之前需要先确认PLC的网络参数。用CX-Programmer或者Sysmac Studio连接PLC查看以下几个参数PLC的IP地址、子网掩码、FINS节点地址。FINS节点地址通常是IP地址的最后一段比如IP是192.168.1.10节点地址就是10。但也可以手动设置只要保证网络中不冲突就行。还需要确认PLC是否启用了FINS通信。有些PLC默认是启用的有些需要手动开启。在CX-Programmer的PLC设置里找到“内置以太网端口设置”或者“FINS/TCP设置”确认FINS服务是开启状态。如果是较新的NJ/NX系列设置方式可能不太一样在Sysmac Studio里找“以太网端口设置”。我建议在正式开发之前先用一个简单的工具测试一下连通性。可以用Python写一个最简单的UDP发送脚本发一条FINS命令过去看PLC有没有响应。这样能快速确认网络通不通、FINS服务开没开、节点地址对不对。如果这一步都通不过后面写再多代码也没用。4.2 用Python实现FINS/UDP读写下面用Python写一个完整的FINS/UDP读写示例。选择Python是因为它上手快、库丰富适合快速验证和原型开发。实际生产环境可以用C#、C或者Java逻辑是一样的。先定义FINS命令帧的构造函数import socket import struct def build_fins_header(da1, sa1, sid, mrc, src): icf 0x80 rsv 0x00 gct 0x02 dna 0x00 da2 0x00 sna 0x00 sa2 0x00 header struct.pack(BBBBBBBBBBBB, icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid, mrc, src) return header def build_read_command(area_code, start_addr, count): # 区域代码 起始地址(低字节在前) 位偏移 读取数量(低字节在前) cmd struct.pack(BHBH, area_code, start_addr, 0x00, count) return cmd def build_write_command(area_code, start_addr, data_bytes): count len(data_bytes) // 2 cmd struct.pack(BHBH, area_code, start_addr, 0x00, count) data_bytes return cmd然后写发送和接收的函数def send_fins_udp(plc_ip, plc_port, fins_frame): sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(2.0) sock.sendto(fins_frame, (plc_ip, plc_port)) try: response, _ sock.recvfrom(4096) return response except socket.timeout: return None finally: sock.close()接下来构造一条完整的读DM区命令。假设PLC的FINS节点地址是10我们要读DM100开始的10个字plc_ip 192.168.1.10 plc_port 9600 da1 10 # 目标节点地址 sa1 0 # 源节点地址UDP方式下可以填0 sid 1 # 服务ID mrc 0x01 src 0x01 header build_fins_header(da1, sa1, sid, mrc, src) read_cmd build_read_command(0x82, 100, 10) # 0x82是DM区代码 fins_frame header read_cmd response send_fins_udp(plc_ip, plc_port, fins_frame) if response: # 解析响应 result_code struct.unpack(H, response[12:14])[0] if result_code 0: data response[14:] values struct.unpack( H * 10, data) print(读取成功:, values) else: print(错误码:, hex(result_code)) else: print(超时无响应)这段代码里有个细节要注意响应帧的前12个字节是FINS头部第12到14字节是结果码第14字节之后才是数据。结果码是两字节大端序。数据部分每个字是两字节大端序。这里跟命令帧的字节序不一样命令帧的地址和数量是低字节在前但响应数据是大端序。这个差异很容易搞混我当初就在这里栽过跟头。4.3 用Python实现FINS/TCP读写TCP方式比UDP多了一个FINS/TCP头的处理而且需要先建立连接、申请节点地址。下面是完整流程def fins_tcp_connect(plc_ip, plc_port): sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5.0) sock.connect((plc_ip, plc_port)) # 发送节点地址申请命令 magic bFINS length struct.pack(I, 12) command struct.pack(I, 0x00000000) error_code struct.pack(I, 0) client_node struct.pack(I, 0) # 请求自动分配 request magic length command error_code client_node sock.send(request) response sock.recv(1024) # 解析分配的节点地址 assigned_node struct.unpack(I, response[16:20])[0] print(分配的节点地址:, assigned_node) return sock, assigned_node拿到节点地址后就可以发送FINS命令了。TCP方式的FINS帧需要加上FINS/TCP头def send_fins_tcp(sock, fins_frame): magic bFINS length struct.pack(I, len(fins_frame) 8) command struct.pack(I, 0x00000002) # FINS帧发送 error_code struct.pack(I, 0) request magic length command error_code fins_frame sock.send(request) response sock.recv(4096) # 跳过FINS/TCP头取FINS响应帧 fins_response response[16:] return fins_responseTCP方式下FINS命令帧里的源节点地址要填PLC分配给你的节点地址不能填0。这个细节很容易忽略填错了PLC会返回错误码。4.4 批量读取的优化技巧实际项目中经常需要读取大量连续地址的数据。如果每个地址都发一条命令效率极低。FINS支持一次读取多个连续字最多可以一次读几百个字。我的经验是把连续的地址合并成一条命令能大幅减少通信次数。比如你要读DM100到DM199这100个字不要分10次每次读10个直接一次读100个。FINS协议支持的最大读取数量跟PLC型号有关一般一次读几百个字没问题。但也不要一次读太多因为UDP包太大会被分片增加丢包风险。我一般控制在200个字以内超过就分多条命令。还有一个技巧是合理安排读取顺序。如果你要读的区域跨越了不同的内存区比如既要读DM区又要读CIO区那就没办法合并只能分开读。所以在PLC程序规划阶段尽量把需要频繁读取的数据放在连续的区域里这样上位机可以用最少的命令读到最多的数据。5. 常见问题排查与避坑经验实录5.1 通信超时无响应的排查思路这是最常见的问题发出去的命令石沉大海一点响应都没有。排查思路按以下顺序来第一步确认网络连通性。用ping命令测试PLC的IP能不能通。如果不通检查网线、交换机、IP配置。这一步虽然简单但确实有很多人栽在这里。第二步确认FINS服务是否开启。有些PLC默认不开FINS服务需要在设置里手动开启。用CX-Programmer连上PLC在PLC设置里找“内置以太网端口”或者“FINS/TCP”相关选项确认是启用状态。第三步确认端口号。FINS/UDP默认端口是9600FINS/TCP默认端口也是9600。但有些PLC可以修改端口号如果你改过记得用改后的端口。第四步确认节点地址。目标节点地址DA1必须跟PLC的FINS节点地址一致。如果PLC的节点地址是10你填了1那肯定没响应。这个错误很隐蔽因为报文格式看起来完全正确但PLC就是不搭理你。第五步确认命令帧格式。用抓包工具抓一下发出去的报文逐字节对照FINS协议规范检查。重点检查长度字段、命令码、区域代码、地址字节序。我遇到过好几次都是长度字段算错了导致PLC直接丢弃报文。5.2 返回错误码的快速对照PLC有响应但返回了错误码说明通信链路是通的问题出在命令内容上。下面整理几个常见的错误码错误码含义可能原因0x0000正常无0x0001命令码不支持MRC/SRC填错了0x0002区域代码不支持区域代码填错了0x0003地址越界起始地址数量超出了区域范围0x0004数据长度不匹配写入数据长度跟数量字段不一致0x0005命令格式错误报文结构有问题0x000B无法执行PLC当前状态不允许该操作0x0010节点地址错误DA1填错了0x0011网络地址错误DNA填错了遇到错误码不要慌先对照表格定位大致方向然后逐项检查。我个人的经验是0x0003和0x0005出现频率最高。0x0003通常是地址算错了比如DM区只有1000个字你从999开始读10个那就越界了。0x0005通常是报文格式有问题比如长度字段不对、字节序反了。5.3 数据读写异常的排查技巧有时候通信正常、错误码也是0但读出来的数据不对。这种情况通常是字节序或者数据类型的问题。FINS协议里命令帧的地址和数量是低字节在前但响应数据是大端序。这个差异我前面强调过但还是要再提一次因为真的太容易搞错了。比如你读到一个字的值是0x1234在响应报文里就是0x12 0x34大端序。但如果你读的是32位整数那就要把两个字拼起来而且要注意哪个是高字哪个是低字。还有一个坑是位寻址和字寻址的区别。FINS支持按位读写也支持按字读写。按位读写的时候位偏移字段要填具体的位号0到15读取数量是位数。按字读写的时候位偏移填0x00读取数量是字数。如果你搞混了读出来的数据会完全不对。比如你想读DM100的第3位结果按字读了那读到的就是整个DM100的值不是那一位的状态。5.4 多上位机并发访问的注意事项产线上经常有多台上位机同时采集同一台PLC的数据。这种情况下UDP方式比TCP方式更合适因为TCP有连接数限制。但UDP方式下多台上位机同时发命令PLC的响应可能会串。这时候SID字段就很重要了每台上位机用自己的SID序列收到响应后先检查SID是否匹配不匹配就丢弃。还有一个问题是PLC的处理能力有限。如果多台上位机同时高频发命令PLC可能来不及处理导致丢包或者响应延迟。我的建议是多上位机场景下每台上位机的采集频率不要太高比如200毫秒以上。如果确实需要高频采集可以考虑用一台中间服务器统一采集然后分发给其他上位机。6. 进阶应用与性能优化建议6.1 用批量命令提升采集效率前面提到过批量读取这里再展开讲一下。FINS协议支持一次读取多个连续字这个功能用好了能大幅提升效率。我做过一个测试读100个字分10次每次读10个总耗时大概是单次读100个的3到4倍。因为每次通信都有固定的开销包括报文构造、网络传输、PLC处理、响应解析。批量读取把这部分开销摊薄了。但批量读取也不是越多越好。UDP包有大小限制一般建议单包不超过1472字节以太网MTU 1500减去IP头和UDP头。FINS命令帧头部12字节读命令数据6字节响应帧头部12字节结果码2字节数据部分每个字2字节。算下来一次读200个字响应包大概是122400414字节完全没问题。读500个字就是1014字节也还行。但读1000个字就超过1400字节了有分片风险。所以我一般控制在200到500个字之间。6.2 心跳机制与断线重连工业现场的网络环境不一定稳定偶尔断线是正常的。如果你的采集程序没有断线重连机制断一次就得手动重启那运维成本就高了。我的做法是加一个心跳机制每隔几秒发一条读命令如果连续几次超时就判定为断线然后触发重连逻辑。TCP方式下断线后socket会报错捕获异常后重新建立连接就行。UDP方式下没有连接的概念只能靠超时来判断。我一般设置超时时间为1到2秒连续3次超时就认为断线然后重新初始化socket。重连的时候要注意如果是TCP方式需要重新申请节点地址。6.3 数据缓存与变化上报如果你的上位机需要把数据上传到数据库或者云平台不建议每次采集都直接写库。因为PLC的数据变化频率可能不高但采集频率很高每次都写库会产生大量重复数据。我的做法是在内存里维护一份数据缓存每次采集后跟缓存对比只有变化的数据才上报。这样能大幅减少数据库写入压力。具体实现可以用一个字典或者数组来存缓存每次读到新数据后逐项对比。如果某个地址的值跟缓存不一样就标记为变化然后只上报变化的部分。这个逻辑不复杂但效果很明显。我之前做过一个项目采集频率是100毫秒一次但实际数据变化频率大概是几秒一次用了变化上报之后数据库写入量减少了90%以上。6.4 安全防护的基本考量工业现场的网络虽然通常是内网但也不能完全不设防。FINS协议本身没有认证和加密机制任何能访问到PLC网络的人都可以读写PLC数据。所以基本的防护措施还是要做的。首先PLC网络跟办公网络要隔离不要直接连通。其次如果条件允许在交换机上做端口隔离或者VLAN划分限制只有采集服务器能访问PLC。再次采集服务器本身要做好安全加固不要在上面装无关软件定期更新补丁。最后如果PLC支持可以关闭不必要的服务只保留FINS通信。这些措施看起来简单但很多现场都忽略了。我见过一个项目PLC直接连在办公网上任何人用个脚本就能读写PLC数据安全隐患很大。后来整改的时候加了防火墙规则只允许采集服务器的IP访问PLC的FINS端口才算把风险降下来。6.5 不同PLC型号的兼容性处理欧姆龙PLC型号很多从早期的CS/CJ系列到后来的NJ/NX系列FINS协议的基本命令是兼容的但有些细节不一样。比如NJ/NX系列支持更丰富的内存区域地址范围也更大。有些老型号不支持EM区或者EM区的Bank数量有限。做项目的时候最好先确认PLC的具体型号和固件版本然后查对应的FINS命令手册。不同型号的手册里会列出支持的命令码和区域代码。如果你写的程序要适配多种型号建议把区域代码和地址范围做成配置项不同型号用不同的配置。这样换PLC的时候只需要改配置不用改代码。我在实际项目中就遇到过这个问题。同一个程序在CJ系列上跑得好好的换到NJ系列上就读不到EM区数据了。后来查手册才发现NJ系列的EM区地址范围跟CJ系列不一样需要调整起始地址。这种细节问题不查手册根本发现不了。6.6 调试工具与抓包分析调试FINS协议的时候抓包工具是必不可少的。Wireshark可以抓取网络报文然后逐字节分析FINS命令和响应。我一般会先用Wireshark抓一次正常的通信过程然后对照报文结构逐字段分析确认每个字段的值是否正确。除了Wireshark还可以用欧姆龙官方的FINS通信测试工具。有些版本的CX-Programmer或者Sysmac Studio自带FINS通信测试功能可以直接发命令、看响应非常方便。如果没有官方工具也可以自己写一个简单的测试脚本把命令帧打印出来逐字节对照协议规范检查。我个人的习惯是每次遇到通信问题先抓包然后对照协议规范逐字段检查。大部分问题都能通过这种方式定位。比如有一次读不到数据抓包后发现响应帧的结果码是0x0003对照表格发现是地址越界检查代码发现起始地址算错了。这种问题如果不抓包光看代码很难发现。6.7 性能测试与调优实录最后分享一下我做过的性能测试数据。测试环境是一台欧姆龙CJ系列PLC通过以太网连接一台工控机用Python脚本做FINS/UDP读写测试。读操作测试单次读10个字平均耗时约5毫秒单次读100个字平均耗时约8毫秒单次读500个字平均耗时约15毫秒。可以看到读取数量增加耗时增加并不明显说明批量读取的效率优势很明显。写操作测试单次写10个字平均耗时约6毫秒单次写100个字平均耗时约10毫秒。写操作比读操作稍慢因为PLC需要处理写入逻辑。连续采集测试以100毫秒为间隔连续采集1000次总共耗时约102秒平均每次约102毫秒。其中通信耗时约8毫秒其余时间是Python脚本的处理和等待时间。如果把采集间隔缩短到10毫秒平均每次耗时约15毫秒说明Python脚本的处理能力还有余量。这些数据仅供参考实际性能跟PLC型号、网络环境、上位机性能都有关系。但总体来看FINS协议的性能完全能满足大多数工业采集场景的需求。关键是要用好批量读取和变化上报这两个优化手段能大幅降低通信次数和数据处理量。踩了这么多坑我的体会是FINS协议本身不复杂但细节很多每一个细节没注意到都可能导致通信失败。最好的学习方式就是动手实践先用最简单的命令跑通然后逐步增加复杂度。遇到问题先抓包对照协议规范逐字段检查大部分问题都能自己解决。实在搞不定的时候查手册、查资料、问同行别自己硬扛。工业通信这个领域经验比理论重要多踩几次坑就熟了。