MIL开发实践(3)——MIL触发采图

前言

接上面的功能完成之后,接下来就要完成触发采图了,这个触发采图耗费的时间比上面那个要长多了,主要原因还是在于对使用的函数理解不够深刻,后面听我细细道来。
首先,我们最主要使用的是这个回调函数,

MdigHookFunction(MilDigitizer, M_GRAB_FRAME_START, ProcessingFunction, &UserHookData);
MdigControl( MilDigitizer, M_GRAB_TRIGGER, M_ENABLE);
MdigControl( MilDigitizer, M_GRAB_TRIGGER_SOURCE, M_SOFTWARE);

这个函数会在程序有采图操作的时候并且有触发操作时执行ProcessingFunction这个函数。采图函数的话,我们使用的是MdigContinuous,这个采图函数可以进行连续采图,不过,它也有它比较独特的特点,具体看这篇文章,它的特点是MdigGrabContinuous为异步采集模式,在采集过程中是直接送到显存中的,不保存在MilBufferImage中,只有停止采集后的最后一帧保存在MilBufferImage中,一般用于实现在线观测功能,要想实现实时抓取图像和处理只能采用MdigGrab和MdigProcess函数

效果图

在这里插入图片描述
因为这里的相机没有加光源和镜头,所以整体的效果看起来就是这样。

正文

打开回调采图模式

首先,我们要先打开回调采图模式。也就是上面的打开TriggerMode的这个操作,这个操作执行了下面的这个函数:
GrabThreadStart

qint32  MDeviceE2v::GrabThreadStart()
{
    qint32 ret = RETURN_FAIL;
#ifdef WIN32_E2v
    MIL_INT WidthVal = 0;
    //下面的这个语句是获得相机参数的语句,但是对于CL接口的相机是不能使用的,CXP接口的才能使用
    //MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("Width"), M_TYPE_INT64, &WidthVal);
    //    MdigControl(MilDigitizer, M_GRAB_TRIGGER_SOURCE, M_SOFTWARE);
    //    MdigControl(MilDigitizer, M_GRAB_TRIGGER_STATE, M_ENABLE);
    MdigGrabContinuous(MilDigitizer, MilImage);
    MdigHookFunction(MilDigitizer, M_GRAB_FRAME_END, ProcessingFunction, &UserHookData);
    MdigControl( MilDigitizer, M_GRAB_TRIGGER, M_ENABLE );
    MdigControl( MilDigitizer, M_GRAB_TRIGGER_SOURCE, M_SOFTWARE);
    MdigControl( MilDigitizer, M_GRAB_MODE, M_ASYNCHRONOUS );
    MIL_INT GrabContinuousEndTrigger = 0;
    //    MIL_INT GrabTriggerActivation = 0;
    //    MIL_INT GrabTriggerSource = 0;
    //    MIL_INT GrabTriggerState = 0;
    MdigInquire(MilDigitizer, M_GRAB_CONTINUOUS_END_TRIGGER, &GrabContinuousEndTrigger);
    //    MdigInquire(MilDigitizer, M_GRAB_TRIGGER_ACTIVATION, &GrabTriggerActivation);
    //    MdigInquire(MilDigitizer, M_GRAB_TRIGGER_SOURCE, &GrabTriggerSource);
    //    MdigInquire(MilDigitizer, M_GRAB_TRIGGER_STATE, &GrabTriggerState);
    qDebug()<<"GrabContinuous is"<<GrabContinuousEndTrigger;
    if (GrabContinuousEndTrigger == M_DEFAULT) // Specifies the default value.
    {
        qDebug()<<"GrabContinuousEndTrigger is M_DEFAULT";
    }
    else if (GrabContinuousEndTrigger == M_DISABLE) // Specifies not to generate the trigger automatically.
    {
        qDebug()<<"GrabContinuousEndTrigger is M_DISABLE";
    }
    else if (GrabContinuousEndTrigger == M_ENABLE) // Specifies to generate the trigger automatically.
    {
        qDebug()<<"GrabContinuousEndTrigger is M_ENABLE";
    }
    MIL_INT AllocationOverscan = 0;
    MsysInquire(MilSystem, M_ALLOCATION_OVERSCAN, &AllocationOverscan);
    if (AllocationOverscan == M_DEFAULT) // M_DEFAULT.
    {
        qDebug()<<"AllocationOverscan M_DEFAULT";
    }
    else if (AllocationOverscan == M_DISABLE) // Specifies that image buffers allocated on the system will have no overscan region.
    {
        qDebug()<<"AllocationOverscan M_DISABLE";
    }
    else if (AllocationOverscan == M_ENABLE) // Specifies that image buffers are allocated on the system with an overscan region.
    {
        qDebug()<<"AllocationOverscan M_ENABLE";
    }
    ret = RETURN_OK;
#endif
    return ret;
}

下面稍微解释一下这个函数:

  1. 首先,毫无疑问,我们必须先把回调函数先执行了,不然,后面你即使进行了触发也没办法采集到你想要的图像。回调函数是这句:
MdigHookFunction(MilDigitizer, M_GRAB_FRAME_START, ProcessingFunction, &UserHookData);
  1. 接下来,肯定是要对触发模式进行设置,要设置说你要的触发到底是软触发还是硬触发,是否马上开启触发等命令:
MdigControl( MilDigitizer, M_GRAB_TRIGGER, M_ENABLE );#触发使能命令
MdigControl( MilDigitizer, M_GRAB_TRIGGER_SOURCE, M_SOFTWARE);#软触发还是硬触发
MdigControl( MilDigitizer, M_GRAB_MODE, M_ASYNCHRONOUS );# 同步命令

这些命令我是从哪里知道的呢?你需要去看文档或者去看
在这里插入图片描述
这个地方有挺多关于这种操作应该执行什么命令,应该会比你使用文档去查看相对来说会要方便一点。

  1. 接下来,就是在这里开启连续采图的操作了。不然,你的触发就没有任何的作用了。用的抓图函数是连续抓图函数。
    MdigGrabContinuous(MilDigitizer, MilImage);
  2. 接下去的内容基本就是属于扩展了,在我这里并没有实际的用处,但也许对你有用,我也稍微讲一下,里面有这个语句:
MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("Width"), M_TYPE_INT64, &WidthVal);

这个语句被我注释掉了,并且也有进行了说明,我在查看文档的时候以为这个是可以读取相机参数的命令,但经过详细查看,发现是不行的,一旦执行这个语句,程序就会崩溃,关于读取相机参数的操作可以看一下我的第四篇文章会讲关于这部分的内容。因为我使用的接口是CL的,也就是CameraLink的,而这个接口只能被CoaXPress接口所使用,关于这个接口的定义是这样的:

CoaXPress (CXP) 是一项同轴电缆不对称高速串行通信标准,是专为机器视觉行业开发的一种数字接口规范。 对于需要高分辨率成像以及图像快速传输到主机的机器视觉应用,该标准的高速高带宽数据传输能力可谓理想的解决方案。
但我没用到就没办法了。所以,如果你的是CXP接口的话,可以尝试一下使用这个接口,就没必要按照我第四篇那么麻烦的方法去读取相应的参数以及采用串口的方式去设置参数了。

  1. 接下来的这个函数是这个:
MdigInquire(MilDigitizer, M_GRAB_CONTINUOUS_END_TRIGGER, &GrabContinuousEndTrigger);

这个函数是用来读取MIL这个采集卡的参数的,你要注意,它的返回值与我们平常用的不太一样,虽然它是使用MIL_INT进行声明的,但与Int差别不是一点点,它返回的东西很多都是对应的ComBox里面的值。所以,如果你要用到MIL的参数的话,你可以使用这个函数进行读取。关于这个函数的第二个参数,你还是需要去看上面第二点讲的那个地方的那个截图就是了,你找到里面你想要的参数打开。
在这里插入图片描述

关闭回调采图模式

先给出完整的关闭函数:

qint32  MDeviceE2v::GrabThreadStop()
{
    qint32 ret = RETURN_FAIL;
#ifdef WIN32_E2v
    qDebug()<<"MDeviceE2v::GrabThreadStop123";

    if(m_bLoaded&&m_cTriggerFlag)
    {
        MdigHalt(MilDigitizer);
        qDebug()<<"-->GrabThreadStop"<<m_bLoaded<<m_bStopWork;
        MdigHookFunction(MilDigitizer, M_GRAB_FRAME_END+M_UNHOOK, ProcessingFunction, &UserHookData);
        m_cTriggerFlag = false;
        m_cChangeModeFlag2 = false;
        ret = RETURN_OK;
    }

#endif
    return ret;
}

下面解析比较重要的几个点:

  1. 首先,比较重要的是一定要对相机的采图操作进行关闭操作。不然,你是无法进行其他的采图函数进行采图的。就是下面的这个语句:
MdigHalt(MilDigitizer);
  1. 然后就是一定要关闭之前开启的回调模式,不然,你如果进行实时采图的话,也会自动去调用这个回调函数。关闭如下:
 MdigHookFunction(MilDigitizer, M_GRAB_FRAME_END+M_UNHOOK, ProcessingFunction, &UserHookData);

关闭采图的相机等

关于关闭的话,这边也顺便讲一下,关于这个Digitizer,Display的打开,你需要在你程序的析构函数,在你关闭你程序的时候执行的析构函数上面进行关闭,不然就一定会报错,不是QT报的错,而是MIL报的错。我关闭的语句是:
~MDeviceE2v

MDeviceE2v::~MDeviceE2v()
{
    qDebug()<<"~MDeviceE2v()";
#ifdef WIN32_E2v
    m_fGrabCallbackEx =Q_NULLPTR;
    m_fXmlChange =Q_NULLPTR;
    m_fSettingChange =Q_NULLPTR;
    m_fException =Q_NULLPTR;
    SrcImageDataPtr = NULL;
    PhysicsImagePtr = NULL;
    MdigHalt(MilDigitizer);
    if (M_NULL != MilImage)
    {
        MbufFree(MilImage);
    }

    MdigHalt(UserHookData.MilDigitizer);
    MdispFree(MilDisplay);
    m_pSerial->close();
#endif
}

打开采图模式

这个操作其实是在你打开了回调模式之后就会接下去执行的一个操作,也就是打开采图。这个操作跟之前的实时采图是同一种方式,下面贴出代码就可,就不去详细讲了,如果有需要的,可以去看我的第二篇文章:

//开启采图过程
qint32  MDeviceE2v::AcquisitionStart()
{
    qint32 ret = RETURN_FAIL;
    int nGrabScaleSet = 4;//设置的采集比例
    qDebug()<<"this"<<this;
#ifdef WIN32_E2v
    qDebug()<<"MDeviceE2v::AcquisitionStart123 m_bLoaded and m_bStopWork"<<m_bLoaded<<m_bStopWork;
    if(m_bLoaded)
    {
        if(m_bStopWork)
        {
            if (MilDigitizer)
            {
                UserHookData.MilImageDisp = MilImage;//这行代码一但注释掉就报错
                UserHookData.MilDigitizer = MilDigitizer;
                UserHookData.ProcessedImageCount = 0;
                MgraFont(M_DEFAULT, M_FONT_DEFAULT_LARGE);
                /*这部分会在图像中绘制一个不旋转或不填充颜色的矩形,或将其添加到图形列表中。
                MgraText(M_DEFAULT, MilImage, (BufSizeX/8)*2, BufSizeY/2,
                         MIL_TEXT(" Welcome to MIL !!! "));
                MgraRect(M_DEFAULT, MilImage, ((BufSizeX/8)*2)-60, (BufSizeY/2)-80,
                         ((BufSizeX/8)*2)+370, (BufSizeY/2)+100);
                MgraRect(M_DEFAULT, MilImage, ((BufSizeX/8)*2)-40, (BufSizeY/2)-60,
                         ((BufSizeX/8)*2)+350, (BufSizeY/2)+80);
                MgraRect(M_DEFAULT, MilImage, ((BufSizeX/8)*2)-20, (BufSizeY/2)-40,
                         ((BufSizeX/8)*2)+330, (BufSizeY/2)+60);
                */
			   MbufInquire(MilImage, M_HOST_ADDRESS, &SrcImageDataPtr);//数据指针
                MbufInquire(MilImage,M_PHYSICAL_ADDRESS,&PhysicsImagePtr);//数据物理地址指针,仅供主线意外的
                MbufInquire(MilImage, M_SIZE_BYTE, &SrcImageDataSize);//数据大小
                ret = RETURN_OK;
                m_bStopWork =false;
            }
        }
    }
#endif
    return ret;
}

上面这个只是为了结构的完整性,只是在这个相机上面没有进行使用,不然,在其他相机上面很多都是有这个操作的。具体可以去看我关于大恒相机的操作。

触发操作

打开触发模式之后,自然而然就是进行触发了,这里我们采用的软触发,使用TriggerSoftware这个按钮进行控制,函数为TriggerSoftwareExecute,下面进行详细讲解:
TriggerSoftwareExecute

qint32  MDeviceE2v::TriggerSoftwareExecute()
{
    qint32 ret = RETURN_FAIL;
    qDebug()<<"m_fGrabCallbackEx6"<<m_fGrabCallbackEx;
    m_bStopWork = true;

    if(MilDigitizer!=M_NULL)
    {
        MdigControl(MilDigitizer, M_GRAB_TRIGGER_SOFTWARE,1);
    }
    ret = RETURN_OK;
    return ret;
}

按下按钮后,执行这个操作,里面最主要的也就只有一个语句了:
MdigControl(MilDigitizer, M_GRAB_TRIGGER_SOFTWARE,1);,这个语句就是进行触发一次的函数。触发好后,这个时候就执行那个回调函数了,终于该它登场了,还记得我们一开始打开的触发模式嘛?对,就是那个函数,里面有个ProcessingFunction函数,请接下去看。

执行回调函数

接下来,开始执行回调函数,完整的回调函数如下:

MDeviceE2v *globalPoint;

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
{
    HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
    MIL_ID ModifiedBufferId = 0;
    //得到buffer 如果
    MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);
    qDebug()<<"ModifiedBufferld HookType"<<ModifiedBufferId<<HookType;//先确定被修改的bufferID,找个HookType是一个字符串
    //处理完的Buffer数据复制到现实buffer
    MbufCopy(ModifiedBufferId,UserHookDataPtr->MilImageDisp);

    UserHookDataPtr->ProcessedImageCount++;
    globalPoint->triggerEvent(UserHookDataPtr);
    return 0;
}
void   MDeviceE2v::triggerEvent(HookDataStruct  *UserHookDataPtr)
{
#ifdef WIN32_E2v
    if(m_bStopWork)
    {
        qDebug()<<"triggerEvent m_bStopWork is true";
        //return;
    }
    MIL_INT BufSizeX2    = 0;
    MIL_INT BufSizeY2    = 0;
    MIL_INT SrcImageDataSize2 = 0;
    void  *SrcImageDataPtr2 = NULL;
    if(UserHookDataPtr->MilDigitizer&&UserHookDataPtr->MilImageDisp)
    {
        MdigInquire(UserHookDataPtr->MilDigitizer, M_SIZE_X,    &BufSizeX2);
        MdigInquire(UserHookDataPtr->MilDigitizer, M_SIZE_Y,    &BufSizeY2);
        MbufInquire(UserHookDataPtr->MilImageDisp, M_SIZE_BYTE, &SrcImageDataSize2);//数据大小
        MbufInquire(UserHookDataPtr->MilImageDisp, M_HOST_ADDRESS, &SrcImageDataPtr2);//数据指针
        qDebug()<<"triggerEvent"<<BufSizeX2<<BufSizeY2<<SrcImageDataSize2<<SrcImageDataPtr2;
        qDebug()<<"m_fGrabCallbackEx3"<<m_fGrabCallbackEx;

        if(m_fGrabCallbackEx)
        {
            MFrameInfo info;
            info.nWidth  = BufSizeX2;
            info.nHeight = BufSizeY2;
            info.nFramerLen = SrcImageDataSize2;
            //info.cFormat = pFrameInfo.getImagePixelFormat();
            uchar *pbit = (uchar*)SrcImageDataPtr2;//得到指向Buffer的地址
            qDebug()<<"pBit"<<pbit;
            qDebug()<<"m_pCallUser"<<m_pCallUser;
            m_fGrabCallbackEx(m_pCallUser,pbit,&info);
        }
        else
        {
            qDebug()<<"m_fGrabCallbackEx is null";
        }
    }
    else
    {
        qDebug()<<"UserHookDataPtrb is Null";
    }

#endif
}

下面进行详细讲解:

  1. 首先,你可能会疑惑,这个函数的三个参数到底是从哪里传进行的,命名之前的那个MdigHookFunction只传了这个函数的函数名而已,怎么会三个参数就都进去了,这跟我们之前学的形参和实参不太一样, 如果你想知道具体的过程,就去查询文档吧,把函数名输进去就可以了,反正,我这里就解释为:当你执行了这个MdigHookFunction的时候,MIL会自动把三个参数给填进去这样理解就可以了。
  2. 接下来,对传进来的第三个参数进行强转:
HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;

这个HookDataStruct的定义是这样的:

typedef struct
{
    MIL_ID  MilDigitizer;
    MIL_ID  MilImageDisp;
    MIL_INT    ProcessedImageCount;
} HookDataStruct;						//自定义的要传递给回调函数的参数结构

所以,传进来的这个参数是有这三个值的。这也就是我们为啥要对其进行强转了。

  1. 接下来的这个语句是一个获取采集图像的bufferID的语句,起到的是一个测试的功能,并不起多大的作用,可删掉。
MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);
  1. 下面这个语句就是整个操作的核心了。将那个数据再传回我们的插件中,这时候出现的问题是,我们现在的这个回调函数是全局函数,是没办法直接调用我们插件类中的函数的,关于这个知识点,需要自行百度。所以,我这里使用的方式是一个比较菜的一个做法,就是在外面弄一个全局变量,就是上面代码中的MDeviceE2v *globalPoint;,通过这个去指向我们要触发的函数triggerEvent。以前的做法是,在这个回调函数中会有一个这个插件类的指针传进来,不过,这个ProcessFunction没有传这种指针进来就没办法了。
globalPoint->triggerEvent(UserHookDataPtr);
  1. 接下去是这个triggerEvent这个函数的执行过程,主要做的操作是把得到的这个buffer里面的信息和这个buffer的地址传给最上层的显示界面,我这里使用的是以一个回调函数m_fGrabCallbackEx的方式进行传递这些信息。

总结

关于触发采图的话,可能难点就是在之前我在挑选采图函数的时候,我选了MdigProcess,这个可以采图,但它的触发信号是在每次有帧事件的时候就执行了,所以,不是我们想要的触发条件,所以,我们使用MdigHookFunction+MdigGrabContinuous
如果有需要可以去看看我的另外三篇文章。感谢您的观看~

MIL开发实践(1)——开发环境的设置
MIL开发实践(2)——MIL实时采图
MIL开发实践(4)——E2v相机参数

若有错误,欢迎指出,感谢~

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Simulink MIL(Model-in-the-Loop)是一种用于开发和测试控制系统模型的工具。它是MathWorks公司开发的一个功能强大的建模和仿真环境,被广泛应用于各个领域的控制系统设计和开发。 Simulink MIL的使用流程一般包括以下几个步骤:首先,通过Simulink进行模型的建立和设计。Simulink提供了丰富的模型库和仿真工具,用户可以根据需求选择合适的模块组合搭建模型。其次,通过Simulink中的仿真工具对模型进行仿真和调试,以验证模型在不同输入条件下的行为是否符合预期。在仿真过程中,可以通过观察模型的图形化输出结果和变量的变化来判断模型的性能。最后,将开发好的模型与实际的硬件系统进行集成和测试。这一步骤可以通过连接模型与实际硬件的接口进行测试,以确保模型在实际环境下的正确性和可靠性。 Simulink MIL具有多种优点。首先,Simulink MIL可以提高开发过程的效率,因为它能够在早期阶段发现和解决设计中的问题,减少后期修改的风险和成本。其次,Simulink MIL可以提供一个可视化的建模环境,使设计师可以直观地看到模型的行为和动态过程。此外,Simulink MIL还提供了丰富的仿真工具和分析功能,使用户可以更好地理解和优化模型。 总之,Simulink MIL是一个功能强大的模型开发和仿真工具,可以帮助控制系统设计师更快速、高效地开发和测试控制系统模型。它的应用范围广泛,可以在各个领域的控制系统设计中发挥重要作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值