免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

基恩士激光位移传感器上位机开发:C#/C++/VB.net通讯实现

基恩士激光位移传感器上位机开发:C#/C++/VB.net通讯实现 简介这是一套基恩士激光位移传感器的跨语言Demo面向工业自动化领域使用C#、C或VB.NET开发上位机程序的工程师解决与传感器通信、参数读写和测量数据获取等问题。压缩包内共365个文件大小仅1.27MB包含292个htm说明文档、18个dll驱动与依赖库、14个h头文件、8个cpp源码、4个cs示例以及VB工程对应的frm/bas/vbp文件覆盖三种语言的完整工程结构。其中LKG3000_DLL_ver2_10为传感器驱动库htm文档提供API调用说明cpp/cs/bas示例分别演示了P/Invoke、LoadLibrary/GetProcAddress等典型调用方式便于对比三种语言的实现差异同时还可从示例中把握DLL加载异常处理、异步读取、数据格式转换与线程安全等开发要点。已有2142人学习/浏览该资源适合需要快速上手基恩士LKG系列传感器二次开发的嵌入式或工业软件开发者参考后可显著减少查阅手册和调试底层接口的时间。 车间里最常见的一个场景设备装好了基恩士激光位移传感器的数值在触摸屏上跳得挺好但到了上位机这边怎么读都不对或者干脆读不上来。我自己就替现场处理过好几回这种事最后发现十有八九不是传感器的问题而是上位机代码没写对。所以这篇就把基恩士激光位移传感器在C#、C、VB.net三语言下的demo开发从头到尾捋一遍适合刚接触工业上位机开发的工程师也适合那些项目做了一半、卡在通讯环节需要找参考的人。1. 拿到的第一步把位移传感器“变成”一个网络设备1.1 先分清型号和接口别只会找网口基恩士激光位移传感器这个大家族里IL系列、LK-G5000系列、CL-3000系列都是常见选手长得不一样通信方式也不完全一样。有的型号支持模拟量输出直接接PLC的模拟量模块就能用4到20 mA或者0到10 V这种本质上不需要写上位机代码。但如果要做数据记录、远程监控、自动判定良品就走数字通信。目前新一点的型号基本都带Ethernet接口也就是常说的网口走TCP/IP通信。还有一些老型号走RS-232C串口这就涉及串口编程了。写demo之前第一个动作是去官网下载对应型号的通信手册确认三个事有没有以太网口、走TCP还是UDP、默认端口是多少。很多人上来就套网上通用的代码结果型号对不上浪费一整天。1.2 基恩士通讯协议的核心约定ASCII指令加换行符基恩士激光位移传感器的以太网通信大多数系列用的是一条一条的ASCII命令。上位机发一条短指令过去传感器回一串ASCII码格式类似“返回状态 数值 单位”中间可能有逗号或空格分隔。指令通常以CR或者CRLF结尾不同系列、不同固件版本会有细微差别。这一点非常容易踩坑。很多人在网上找到一段别人写好的demo命令发过去返回全是问号或者直接没反应。原因往往是换行符不对或者指令关键字跟当前系列不匹配。我从一个项目上拿到的经验是开发前先把通信手册里的指令表完整看一遍里面会明确写每条指令的格式、返回帧的每个字节代表什么。特别是“读取当前测量值”这条指令有的系列叫MS有的系列用MR还有的直接用类似“LON”这种带参数的写法。代码层面其实很简单难的是命令没选对。1.3 连接前必须确认的三个参数IP地址基恩士传感器出厂默认IP一般是192.168.0.1这一类具体以手册为准。用配置软件或者直接网页登录修改注意和电脑的IP在同一个网段。端口号有些型号固定端口有些可以通过配置软件改。默认值和对应关系手册里都有。触发源传感器是连续测量还是外部触发测量会直接决定上位机读数的频率和意义。连续测量模式下上位机发一条读指令传感器返回当前瞬时值外部触发模式下传感器收到外部信号才测一次上位机如果一直发读指令可能读到一条错误提示。这三个参数没搞对程序写得再漂亮都是白搭。我见过一个现场调试人员折腾了两天最后发现是电脑IP和传感器IP不在同一网段Ping都不通更别提读数据了。2. C#版本WinForm上位机读取和显示实测值C#的demo是需求最旺盛的。毕竟现在新项目里用WinForm做界面很常见C#写起来比MFC舒服太多而且对接数据库、导出Excel都方便。2.1 封装一个精简的连接类把TcpClient放进对的地方C#里读写TcpClient是基本功但很多初学者喜欢把连接代码直接塞进按钮点击事件里一锤子买卖。实际项目里建议封装成一个类连接、断开、发送命令、接收响应都做成方法。这样后续换传感器型号、换协议只改这个类就可以了。public class KeyenceLaserClient { private TcpClient _client; private NetworkStream _stream; private readonly object _lockObj new object(); public bool Connect(string ip, int port, int timeoutMs 500) { try { _client new TcpClient(); IAsyncResult result _client.BeginConnect(ip, port, null, null); bool ok result.AsyncWaitHandle.WaitOne(timeoutMs); if (!ok) { _client.Close(); return false; } _client.EndConnect(result); _stream _client.GetStream(); _stream.ReadTimeout timeoutMs; return _stream.CanWrite _stream.CanRead; } catch { return false; } } public string SendReceive(string command) { lock (_lockObj) { byte[] sendBytes Encoding.ASCII.GetBytes(command \r\n); _stream.Write(sendBytes, 0, sendBytes.Length); _stream.Flush(); byte[] recvBytes new byte[1024]; int len _stream.Read(recvBytes, 0, recvBytes.Length); return Encoding.ASCII.GetString(recvBytes, 0, len).Trim(); } } public void Close() { _stream?.Close(); _client?.Close(); } }注意我给连接加了超时这是仿“生产环境”的常规配置。电脑网线没插好、传感器没通电、IP被占用这些情况如果直接调用Connect在WinForm里会造成界面假死超时机制能避免这个体验问题。2.2 实时刷新UI的正确姿势别在UI线程里收数据第一个版本跑通之后很多人会掉进第二个坑在Timer事件里发指令、读数据、直接更新TextBox。如果传感器响应速度稍慢或者网络有抖动WinForm就会出现“闪退”或者界面卡住这是因为Socket的接收操作阻塞了UI线程。正确做法是用BackgroundWorker或者Task把收发指令的循环放到后台线程拿到结果后用Invoke或者BeginInvoke回UI线程刷新控件。简单说就是连接在后台收发在后台界面只管显示。private async void btnStart_Click(object sender, EventArgs e) { await Task.Run(() { while (_isReading) { string raw _laserClient.SendReceive(MS); double value ParseValue(raw); BeginInvoke((Action)(() lblValue.Text value.ToString(F3))); Thread.Sleep(50); } }); }50毫秒刷新一次是现场比较稳的频率。如果追求更高实时性可以压缩到20毫秒但要看传感器的通信周期否则可能连发命令导致传感器来不及响应。2.3 归零、阈值这些控制命令怎么发读取数值只是第一步现场还经常需要归零、设置判定阈值、启动/停止测量。这类控制命令和读取命令的本质区别是它们往往有执行条件。比如归零得保证传感器已经稳定并且表面没有正在往探头前面移动的物体否则归零后的基准面就是错的。再比如阈值设置好的做法是封装一个方法把测量值、上下阈值都作为参数传进去返回设置成功还是失败然后记录到日志里。我在项目里习惯把所有发送过的命令和返回结果都写进日志这个习惯在调试时救了我很多次。现场排查问题时翻日志比摸传感器可靠得多。2.4 掉线了怎么办断线重连的简单实现工业现场网络环境不像办公室那么干净交换机不稳定、网线被踩、传感器重启都是可能发生的。一个靠谱的上位机程序必须处理断线重连。我的做法是Read操作抛异常时标记当前连接已断开尝试重新Connect重连失败则等几秒再试同时把异常信息抛到UI线程在界面上给一个“通讯断开”的醒目提示。重连成功之后要重新做一次初始化命令比如重新打开测量、恢复之前的阈值配置。这个“恢复现场”的步骤很多新手会漏掉导致重连成功但数据不对。3. C版本适合嵌入工控机和配合运动控制卡C的demo多数出现在两类场景一类是工控机上的老程序以前用MFC写的界面现在要接一台新的激光位移传感器另一类是配合运动控制卡做实时检测数据要直接参与运动控制逻辑C的实时性优势比C#更明显。3.1 Winsock初始化这步最容易被杀毒软件拦怎么处理Windows下C写Socket第一步就是用WSAStartup初始化Winsock。这个函数本身不难但有个让人抓狂的坑某些工控机上装了安全软件WSAStartup返回的不是0而是错误码程序直接崩掉。遇到这种情况先看错误码。如果是WSAEINPROGRESS或者WSAEINVALIDPROVIDER多半是第三方网络驱动或安全软件跟Winsock冲突。处理办法是更新驱动、把程序加入白名单或者用管理员权限运行。代码层面要注意WSAStartup用MAKEWORD(2, 2)请求2.2版本并且一定记得在程序退出时调用WSACleanup否则下次启动可能报“Socket句柄耗尽”。#include winsock2.h #include ws2tcpip.h #include stdio.h #pragma comment(lib, ws2_32.lib) int main() { WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), wsa) ! 0) { printf(WSAStartup failed\n); return -1; } SOCKET sock socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addr { 0 }; addr.sin_family AF_INET; addr.sin_port htons(9000); inet_pton(AF_INET, 192.168.0.1, addr.sin_addr); if (connect(sock, (sockaddr*)addr, sizeof(addr)) SOCKET_ERROR) { printf(connect failed: %d\n, WSAGetLastError()); closesocket(sock); WSACleanup(); return -1; } const char* cmd MS\r\n; send(sock, cmd, (int)strlen(cmd), 0); char buf[256] { 0 }; recv(sock, buf, sizeof(buf), 0); printf(Sensor value: %s\n, buf); closesocket(sock); WSACleanup(); return 0; }inet_pton在老版本Visual Studio里可能要用inet_addr替代这也是环境相关的细节。3.2 命令发送与返回数据解析注意字节序和符号位C里面解析返回数据时最容易栽在“看似数字、实则是ASCII字符串”这个点上。基恩士的返回值通常是一串ASCII字符比如“00234.567”而不是二进制浮点数。所以直接拿int*去强转读取内存地址是行不通的必须先把char数组转成字符串再按业务需求转换。如果传感器返回的是负数字符串里会带负号解析时要用atof这类函数而不是简单的前缀截取。如果数据精度要求高要注意显示位数基恩士有些型号能到0.01微米但前提是你在解析时保留足够的小数位并且确认传感器的显示单位是mm、um还是nm。单位搞错了一个看似离谱的测量结果会让你怀疑整个代码写错了实际上只是单位换算问题。3.3 多线程收数传感器数据频率与工控程序的配合C程序里如果只是简单地在主循环里recv数据频率一高就很容易丢包或者主线程被一次网络超时卡住几百毫秒影响整个控制周期。实际项目里建议给传感器单独开一个接收线程把最近一次的有效测量值存在一个共享变量里主控制循环需要数据时直接从共享变量读。这里要注意的是共享变量的读写要做线程同步最简单的方式是加一个临界区或者用std::atomic修饰防止读到半截数据。我见过程序员在这里不加锁结果测量值偶尔出现“跳变”怎么排查都查不出原因最后发现问题出在多线程访问同一个变量上。3.4 和运动控制卡联动的数据传递思路如果位移传感器要和运动控制卡联动比如测高然后调整压头位置那么数据路径就不能走“传感器到PC再到控制卡”这种串行链路否则延迟很高。正确的思路是PC只负责读取和记录运动控制逻辑放到控制卡内部传感器通过I/O或现场总线直接给控制卡信号。这里C的优势就出来了调用控制卡厂商的SDK通常本身就是C接口C直接对接很顺。建议把读传感器和调用控制卡SDK放在同一个高优先级线程里循环周期控制在10毫秒以内这对传感器的响应速度和控制卡的命令执行时间都有要求需要实测确认。4. VB.net版本老项目里最“省事”的重写选择4.1 遇到的老项目场景还没上位的设备、维护人员只熟VB我知道很多年轻工程师对VB.net有偏见但在工厂里转一圈就会发现大量在役设备的上位机是VB.net甚至VB6写的尤其是十几年前投产、至今还在稳定跑的生产线。这些项目的维护人员可能不懂C#但能看懂VB。所以当这类产线要新增一台激光位移传感器做数据采集时写一个VB.net的demo往往比全部推倒重来更现实。另外有些传感器厂商提供的演示程序样例里同时有VB.net版本说明这个语言在工业圈一直都有需求。既然应用厂在用那代码就得有人写。4.2 一个能跑通的TcpClient代码注意事项写在注释里VB.net的TcpClient用法和C#几乎一样只是语法不同。关键点还是那几个超时设置、数据解析、UI线程切换。Imports System.Net.Sockets Imports System.Text Public Class LaserSensorClient Private client As TcpClient Private stream As NetworkStream Public Function Connect(ip As String, port As Integer) As Boolean Try client New TcpClient() Dim result As IAsyncResult client.BeginConnect(ip, port, Nothing, Nothing) Dim ok As Boolean result.AsyncWaitHandle.WaitOne(500) If Not ok Then client.Close() Return False End If client.EndConnect(result) stream client.GetStream() stream.ReadTimeout 500 Return True Catch ex As Exception Return False End Try End Function Public Function SendReceive(cmd As String) As String Dim sendBytes As Byte() Encoding.ASCII.GetBytes(cmd vbCrLf) stream.Write(sendBytes, 0, sendBytes.Length) stream.Flush() Dim recvBytes(1024) As Byte Dim len As Integer stream.Read(recvBytes, 0, recvBytes.Length) Return Encoding.ASCII.GetString(recvBytes, 0, len).Trim() End Function End Class那段BeginConnect是重点不加超时的话如果IP地址根本不存在UI会卡住几十秒才弹错误。加了超时之后连接失败能快速反馈给操作员而不是干等。4.3 VB.net和C#混调DLL时的坑事件委托与线程切换有些老项目里外购的采集卡SDK只提供C#版本或者C的DLLVB.net要引用它就得走COM Interop或者自己包装一层。这中间最容易踩的坑是DLL回传数据时触发的事件在VB.net里收到的线程不是UI线程直接更新界面控件会抛异常。解决办法是写一个委托在事件回调里用Me.Invoke切回UI线程。这个坑我在C#里也踩过但在VB.net里遇到得更多因为老项目的开发者往往不太注意线程模型控件更新代码习惯性随手就写。5. 三种语言demo都跑通后我总结的避坑清单5.1 同一个系列的命令也可能不一样基恩士激光位移传感器不是一个型号而是一个大家族。同样是读当前测量值IL系列的指令和LK-G5000系列的可能就不同甚至同系列不同固件版本之间都有差异。所以最稳妥的做法是拿到一台传感器先看面板标签上的型号再找对应手册查指令表不要默认“上次那个型号能用这次也一定行”。我在现场就吃过这个亏。上一台传感器用“MS”读数值跑得好好的换了一台同品牌更高精度的型号指令发过去一直返回错误码后来翻手册才知道新系列要求先发一条选择命令进入特定模式后才能读数据。5.2 返回值里有中文和单位编码问题部分传感器返回的数据里带了中文说明文字比如“OK”、“NG”这样的判定或者带单位的字符串。如果你用ASCII解码中文会变成乱码。处理方法是先确认返回数据的编码格式大多数是UTF-8或ASCII但有的历史型号是本地代码页。最简单的办法是用Encoding.Default来解码但要注意这台电脑的Windows区域设置可能会影响结果。更稳妥的是先判断前几个字节是不是固定的数字开头只截取数字部分做解析绕开中文的干扰。这个“只取数字”的思路在工业现场非常实用别管协议里带多少额外信息最终要的其实就是那一个测量值。5.3 关于超时、重试、日志三个建议超时的设置要分场景。读取测量值的命令超时建议设短一点500毫秒足够如果超过1秒还没返回说明链路已经不正常了再等也是白等。但执行归零、校准这类动作超时要放宽到3到5秒因为传感器内部可能需要时间稳定。重试不能无限重试最多连续重试3次每次间隔200毫秒还不行就抛异常并报警通知操作员。无限重试会让程序在传感器断线时陷入死循环界面表现为“卡死”。日志一定要记录。不管用C#、C还是VB.net每发一条指令、每收到一条响应都带上时间戳写入日志文件。定位现场问题时这个日志文件的价值远超任何调试工具。我个人的习惯是日志文件按天切分日志里同时记录原始返回值和解析后的数值。这样即使解析代码本身有bug也能从原始数据里还原真实情况不用返工。本文还有配套的精品资源点击获取
返回列表