公司用halcon机器视觉技术在外地有一个项目,就是找到集装箱地板上的钉子的坐标,然后在这个坐标上用油封住这个钉子。
一,这个项目的框架早就搭建好了,框架采用了生产者 / 消费者模式。
在原来的代码Test()函数中,
// 测试
private void Test(HImage image)
{
_halcon.Find_Nail(image, 0, 0, 1800, 1800, 180, 5000, 15000, 7.5, out HTuple width, out HTuple height);
_formMain.DisplayImageMessage(string.Format("w:{0},H:{1}", width, height));
}
halcon找到了了钉子,并且显示它的坐标。
为了能在用户通过上位机给我们这里传递指令后,我们必须发送还给他们钉子的坐标,我声明了一个全局变量队列 q
public Queue q;
并且修改了TestI()函数
// 测试1 (实际使用)
private Queue Test1(HImage image)
{
_halcon.Find_Nail(image, UpLeftX, UpLeftY, DownRightX, DownRightY, Grayscale, Area1, Area2, Crevice, out HTuple width, out HTuple height);
Queue tempq = new Queue();
for (int i = 0; i < width.Length; i++)
{
strtemp = Math.Round((double)width[i]) + " " + Math.Round((double)height[i]);
tempq.Enqueue(strtemp);
}
return tempq;
}
让它返回一个队列tempq,因为队列是先进先出的,所以只要有外来的指令传过来,总可以把先采集到的坐标发送出去。
public void ReceiveImage(HImage image)
{
_formMain.DisplayImage(image);
q = Test1(image);
_formMain.DisplayImageMessage();
image.Dispose();
}
while (q.Count > 0)
{
message = (string)q.Dequeue();
SplitCoord(message, out int[] x, out int[] y);
var senddata = FormatHelper.FormatSend(x, y);
foreach (var item in senddata)
{
tmp.Append(item.ToString("X2") + " ");
}
_serial.SendData(senddata);
//记录到log中
SaveFile("找到坐标点(" + i +"): " + message);
i++;
}
二,要在屏幕上显示来一条指令,发送还此时的钉子坐标,原来的显示函数是这么写的:
// 显示Log
public void DisplayImageMessage(string message)
{
var len = _screenMessage.Count;
if (len >= Screen_Message_Row)
{
_screenMessage.RemoveAt(0);
}
_screenMessage.Add(message);
var top = 0;
foreach (var m in _screenMessage)
{
hSmartWindowControlMain.HalconWindow.DispText(m,
"window", top, "left", "green", "box", 0);
top += 20;
}
}
但是这样会使屏幕上出现每行都有字的现象,干扰视觉。
我想不如这个函数每次把屏幕清空,然后把整个屏幕(一共十行显示)按照接受到的字符全部显示;而接受到的屏幕字符也采用一个队列,考虑到队列还是先进先出,所以后面的字符会在底下显示,同时把最上面的字清掉。那么接受的字符本身就是就考虑字符超过十行后,会把上面的行清除掉。
//实际使用
public void DisplayImageMessage()
{
string[] tempStr = new string[10] { "", "", "", "", "", "", "", "", "", ""} ;
_screenMessage.Clear();
if (_mainController.mQ.Count > 0)
{
_mainController.mQ.CopyTo(tempStr,0);
for (int i = 0; i < tempStr.Length; i++)
{
_screenMessage.Add((string)tempStr[i]);
}
var top = 0;
foreach (var m in _screenMessage)
{
hSmartWindowControlMain.HalconWindow.DispText(m,
"window", top, "left", "green", "box", 0);
top += 20;
}
}
}
// 连接设备
public void ConnectDevice()
{
var message = "";
try
{
_imageGrabber.OpenGrabber(_programParam.VisionInterfaceName,
_programParam.VisionCameraType, _programParam.VisionDeviceName);
}
catch (Exception e)
{
message = string.Format("连接相机失败:{0}。", e.Message);
if (mQ.Count >= 10)
{
mQ.Dequeue();
mQ.Enqueue(message);
}
else
{
mQ.Enqueue(message);
}
LogHelper.WriteLog(message);
_formMain.DisplayImageMessage();
}
//后台打开串口
var s = _serial.Connect(out string error);
if (s)
{
message = string.Format("打开串口成功: {0}", _param.SerialName);
if (mQ.Count >= 10)
{
mQ.Dequeue();
mQ.Enqueue(message);
}
else
{
mQ.Enqueue(message);
}
LogHelper.WriteLog(message);
_formMain.DisplayImageMessage();
}
else
{
message = string.Format("打开串口失败:{0}" , error);
if (mQ.Count >= 10)
{
mQ.Dequeue();
mQ.Enqueue(message);
}
else
{
mQ.Enqueue(message);
}
LogHelper.WriteLog(message);
_formMain.DisplayImageMessage();
}
LogHelper.WriteLog(message);
}
这样,在这个项目中,我使用了2次队列。