目录
1.导言
庞大的NI LabVIEW软件平台
LabVIEW 平台的特点
海康机器人的视觉算法平台VisionMaster特点
如何在LabVIEW中进行海康视觉算法平台二次开发
2.方法与步骤
初次使用VM4.2在LabVIEW 平台下做二次开发遇到的挫折
正确的使用方式
非界面控件库的封装
在LabVIEW中使用浅封装后的VM SDK
3.更进一步
4.总结
LabVIEW从1983年美国NI公司发布1.0版本以来,已经发展了将近40年,在虚拟仪器领域占据了绝对的统治地位,LabVIEW 已经从单纯的信号采集与显示,图形化的虚拟仪器软件发展为包罗万象,海纳百川的工业软件平台,涵盖了工业控制(如PLC,单片机的开发,运动控制),数据采集与监控(如工业组态软件,人机界面软件),集成电路开发(FPGA开发模块),机器视觉(NI Vision模块),计算机视觉与深度学习(Tensorflow模块),视频与音频处理(人脸识别,语音识别),状态机(一种编程模式),数学建模与仿真(Matlab与Simulink模块)等等。
很多第一次听说LabVIEW或者对LabVIEW了解不多的人,可能会认为LabVIEW是一款编程软件,事实上这种说法好比说微软的Windows是一款编程软件那样可笑(事实上确实有一套基于Windows API的编程,使用Windows API你可以操作Windows平台上的一切),实际上LabVIEW 并不单纯的是一款编程软件或者编程语言,它是一个平台,一个可以运行不同编程语言开发出来的模块的软件平台,图形化的编程是它最大的特点,图形化编程的特点使得电气工程师,机械设计工程师,财经证券分析工程师,生物医学工程师等等这些未经过传统文本编程语言(C/C++,Delphi, VB.net,C#,Java等)训练与熏陶的不同领域的工程师都能够让计算机完成一些自动化的工作。因此LabVIEW在全世界范围内广受工程师的喜爱。
海康机器人(后面简称海康)的视觉算法平台VisionMaster(后面简称VM)在图形化编程方面和LabVIEW推崇的理念一样,就是尽量简化视觉的文本编程,能用图形化表达的,尽量用图像化的模块去表达,将视觉算法的调用过程隐藏在模块的后台,只需要简单的拖拉几个模块,把线一连就能工作,让视觉开发者感受不到后台的复杂的数据结构、函数调用、内存的闪躲腾挪的变戏法一样的复杂操作,因此,即使不是专业的程序员,只有具备基本的逻辑思维能力,非软件开发人员也能轻松驾驭VM的二次开发。
LabVIEW中使用第三方库,有这么几种方。
有一些熟悉LabVIEW开发的用户,按照自己对LabVIEW调用C#动态库的方式的理解,兴冲冲的打开VM的安装路径下的动态库路径,这个路径一般是:
C:\Program Files\VisionMaster4.2.0\Development\V4.x\Libraries\win64\C#
以及
C:\Program Files\VisionMaster4.2.0\Development\V4.x\ComControls\Libraries\win64
然后在LabVIEW中以语言互操作来调用C# 动态库,如下图所示:
图1-使用.net容器
接着在LabVIEW的前面板上,希望放置一个VM的主视图控件(VmMainView)选择上面提到的路径中的DLL,如下图所示:
图2-加载程序集发生错误
相信多数用户到了这一步,内心是绝望的,“尝试加载程序集时发生错误”这是什么鬼?难道VM4.2 的C# 库是不支持LabVIEW的吗?
其实并不是,因为Vm的控件库依赖的很多其他基础库,而这些基础库有一些LabVIEW需要的程序集并不在全局缓存程序集中(也就是所谓的GAC,Global Assembly Collection),导致LabVIEW在加载VM控件DLL,无法将它依赖的程序集一并加载进来,所以出现了上面的报错。
既然LabVIEW直接加载VM原始DLL会报加载程序集发生错误,那如果我把VM的原始C#控件库,做一层浅封装,将VM控件库放在一个用户控件的容器中,让LabVIEW 调用用户控件,这样是不是就可以了呢?答案是肯定的。为啥这种方式是行得通的呢?因为,VM4.2在安装的时候,已经把VM库的运行时依赖写入了系统的环境变量,使用VM二次开发生成的DLL是可以放在系统的任意目录下都能运行的,这样就省去了让LabVIEW在加载时期寻找依赖了。好,那我们说干就干,我们对VM 的SDK做一层浅封装,一提到 SDK库封装,很多初学者要瑟瑟发抖了,内心是拒绝的,啥?要自己封库啊,我要是有封库的本事,我还用用得着调用现成的库啊,我自己开发库好了。
非也,非也,此库封非彼库封装,如果是教用户从底层开始封装SDK,那等于是为了解决一个问题,引入一个新问题,并没有从根本上解决问题。我们只需要对VM的SDK的库函数做一个很浅的封装,简单到令人发指。仅仅就是把VM的库照搬进来,包裹一下,这里用一个形象的比喻来说明这个问题,这就好比海康是生产一种高级白酒的厂家,你从海康买来酒浆原液,然后把酒浆原液倒入自己生产的瓶子里,你自己并不需要参与白酒的复杂酿造过程,甚至你都完全不需要知道这些酒是怎么酿出来的,你唯一需要做的就是把酒倒进你的瓶子里。
好了,我就就从封装VM的MainView控件开始,有多简单呢,告诉你,全程不需要写一行代码,对,就是这么简单粗暴,服没?
首先,新建一个C#的用户控件工程,如下如所示:
图3-创建窗体控件库工程
接着在VS的工具箱中拖动VM的渲染控件到你的控件窗口中,如下图所示:
然后调整此控件的Dock属性为“Fill”,为啥要这样,这样做的目的就是为了让你的控件能够自动适应它的父窗口大小,使用这个控件的用户可以随意调整该控件的大小,而里面的布局始终都是充满窗口的。
好,现在只需要编译一下你的工程,就会生成这个控件DLL了,是不是很简单,全程有没有要你写,哪怕一行代码?有木有?
生成了这个MainView的用户控件DLL,接着就可以在LabVIEW中调用了,如下图所示,我这里生成的MainView控件名字叫VMMainViewControl.dll, 因此我在LabVIEW中使用右键菜单.NET与ActiveX,选择.NET容器,然后插入.NET控件,选择VMMainViewControl.dll,如下图所示:
是不是再也不报加载程序集出错了?接着我们在LabVIEW中运行一下看看,是什么效果,如下图所示:
仅仅是封装MianView控件是不够的,我们还需要实现加载方案,运行流程,获取算法模块结果,获取流程配置的输出结果,获取全局变量结果等等,这些是和界面无关的一些操作API,如果所述,如果你直接在LabVIEW中加载VM.PlatformSDK.dll,VM.Core.dll这样C# dll 你是无法让LabVIEW把这些DLL的API函数列出来的,所以需要新建一个.net 类库工程。
所以,我们新建一个VmOperator类库,将常用的VM SDK的接口API加进来,并将其实现,这个稍微有一点点工作量,但是仍然是浅层封装。
如下图所示,我在VmOperator类中,设计了以下API,并加以实现。
namespace HIKVmSDK
{
public class VmOperator
{
public static int LoadSolution(string solPath,string password);
public static int SaveSolution();
public static int SaveSolutionAs(string solPath,string password);
public static int LoadProcedure(string proPath,string password);
public static int ExportProcedure(string proPath);
public static int RunSolution();
public static int RunProcedure(string procedureName);
public static int RunProcedure(string procedureName,string triggerCommand);
public static int SetModuleParam(string moduleName,string paramName,string paramValue);
public static int GetModuleParam(string moduleName,string paramName, ref string paramValue);
public static int SetGlobalVariable(string variableName,string varaiableValue);
public static int GetGlobalVariable(string variableName,ref string varaiableValue);
public static int GetAllGlobalVariables(ref int count, ref string[] names,ref string[] values);
public static int DestroyInstance();
}
}
这些API的实现并不复杂,都套用一个共同的模板,如下:
public static int xxxxOperation(args)
{
try
{
//调用VM SDK
retrun 0;
}
catch(VmException ex)
{
return ex.GetErrcode();
}
}
所以并不复杂,所以VM SDK的用户完全可以实现这些API接口,按照上面的模板,实现加载方案的代码如下图所示:
图7 - 加载方案代码
运行流程的代码也不复杂,如下图所示:
图8-运行流程代码
有一点需要注意的是,VMSolution这个全局对象的销毁的操作是必须要实现的,实现的代码也非常简单,如下图所示:
图9-释放资源代码
有了前面已经封装好的DLL,在LabVIEW中调用就变得非常简单了,熟悉LabVIEW编程就可以按部就班来调用VM SDK了,这里举一个简单的例子,新建一个LabVIEW VI文件,我们在VI前面板中放置一个ManiView控件和VmRender渲染控件,放置几个按钮,实现加载方案,运行方案,渲染方案的简单功能。如下图所示:
LabVIEW 的VI后面板的程序框图实现加载方案操作,如下所示:
图11-加载方案操作程序框图
加载方案加载方案后控制流程运行一次并渲染流程的结果显示,实现如下图所示:
图12- 流程运行一次并渲染流程1
这里需要格外引起注意的是,一定要在VI程序结束运行时,释放全局对象VmSolution, 不然会引起LabVIEW崩溃,我相信即使是精通LabVIEW编程的开发者,在这里都会栽跟斗。所以,这就是前文中提到的为啥一定要实现DestroyInstance这个接口。
好了,让我们看一下整体的运行效果,如下图所示:
图13-VI程序运行效果
既然LabVIEW调用VM SDK必须以用户控件的方式调用,我们何不直接先用VisualStudio封装一个集成各种VM操作和显示的独立用户控件呢,这样不是更省事,事实上,这也是我们推荐的做法。
如下所示,我把VM的常见操作,结果的获取都封装在一个控件里了,也就不再需要单独封装一个个控件,如下图所示:
图14-封装应用控件,而不是封装具体控件,灵活性更大
为了使更加通用,我把模块的参数的调试的二级弹出窗口也做了封装,这样调试在LabVIEW中,就不必弹出子VI,而是弹出WinForm窗口,这样就简单多了,如下图所示:
让我们来看一下整体的运行效果,如下图所示:
图16-整体运行效果
使用这种方式调用VM控件,LabVIEW的程序框图就得到了极大的简化,后置面板的程序框图,简单到令人发指,如下图所示:
图17-极简VM SDK调用方式