当前位置: 首页 > news >正文

3:QT联合HALCON编程—海康相机SDK二次程序开发

思路:

1.定义带UI界面的主函数类

         1.1在主函数中包含其它所有类头文件,进行声明和实例化;使用相机时,是用公共相机的接口在某一个具体函数中去实例化具体的海康相机对象。

        1.2设计界面:连接相机,单次采集,连续采集,停止采集,关闭相机

        1.3在连续采集中,开始启用线程。需要在线程类中采集图像,通过信号与槽在主函数中接收并显示图像。

2.定义公共相机接口类

3.定义海康相机类

4.定义线程类

        4.1里面包含:接收海康相机的函数,在主函数中可以直接调用线程里面的这个函数,并通过指针传参。

        4.2定义run函数,在函数里面采集图像,并发送信号。

         4.3定义if语句判断线程状态,控制线程启停。

        4.4定义bool变量去结合if进行判断

5.定义日志类

6.定义加载保存参数,公共结构体类

7.定义其它图像处理功能类

        7.1例如:测量,匹配,检测等...............


1.主目录框架

列表中含有:

1.src里面是图像处理的各种模块功能以及公共文件(日志和参数加载)

2.相机的公共接口类

3.Halcon类

4.海康相机类

5.主函数

6.线程类


2.定义公共相机接口(camerainterface)

camerainterface.h

#ifndef CAMERAINTERFACE_H
#define CAMERAINTERFACE_H#include "HalconCpp.h"
#include "HDevThread.h"
using namespace HalconCpp;
#include"iostream"
class CameraInterface
{
public:CameraInterface();~CameraInterface();//关闭相机采集 // ch:连接设备virtual int connectCamera(int id)=0;//设置是否为触发模式virtual int setTriggerMode(unsigned int TriggerModeNum)=0;//开启相机采集virtual int startCamera()=0;virtual int stopCamera()=0;//关闭相机virtual int closeCamera()=0;//软触发virtual int softTrigger()=0;//读取buffervirtual int ReadBuffer(HObject &image)=0;//设置图像高度virtual int setHeight(unsigned int height)=0;//设置图像ROI宽度virtual int setWidth(unsigned int width)=0;//获取图像高度值virtual int getHeight()=0;//获取图像宽度值virtual int getWidth()=0;//获取相机曝光时间virtual float getExposureTime()=0;//设置图像水平偏移OffsetXvirtual int setOffsetX(unsigned int offsetX)=0;//设置图像竖直偏移OffsetYvirtual int setOffsetY(unsigned int offsetY)=0;//设置触发源virtual int setTriggerSource(unsigned int TriggerSourceNum)=0;//设置帧率控制使能virtual int setFrameRateEnable(bool comm)=0;//设置心跳时间virtual int setHeartBeatTime(unsigned int time)=0;//设置曝光时间virtual int setExposureTime(float ExposureTimeNum)=0;//设置增益virtual int setGain(float Gain)=0;//关闭自动曝光virtual int setExposureAuto(bool exposureAutoFlag)=0;//关闭自动增益virtual int setGainAuto(bool gainAutoFlag)=0;//virtual int setGain(float Gain)=0;//清理相机缓存virtual void clearBuffer()=0;
};#endif // CAMERAINTERFACE_H

3.定义海康相机类(hikvisionsdk)

1.hikvisionsdk.h文件,海康相机功能函数声明(包含公共相机头文件)

#ifndef HIKVISIONSDK_H
#define HIKVISIONSDK_H
#include"camerainterface.h"
#include"MvCameraControl.h"
#include "HalconCpp.h"
#include "HDevThread.h"
#include <QImage>
using namespace HalconCpp;
class HikvisionSDK:public CameraInterface
{
public:HikvisionSDK();~HikvisionSDK();// ch:连接设备//int connectCamera(std::string id);int connectCamera(int id);//设置是否为触发模式int setTriggerMode(unsigned int TriggerModeNum);//开启相机采集int startCamera();//关闭相机采集int stopCamera();//关闭相机int closeCamera();//软触发int softTrigger();//读取bufferint ReadBuffer(HObject &image);//设置图像高度int setHeight(unsigned int height);//设置图像ROI宽度int setWidth(unsigned int width);//获取图像高度值int getHeight();//获取图像宽度值int getWidth();//获取相机曝光时间float getExposureTime();//设置图像水平偏移OffsetXint setOffsetX(unsigned int offsetX);//设置图像竖直偏移OffsetYint setOffsetY(unsigned int offsetY);//设置触发源int setTriggerSource(unsigned int TriggerSourceNum);//设置帧率控制使能int setFrameRateEnable(bool comm);//设置心跳时间int setHeartBeatTime(unsigned int time);//设置曝光时间int setExposureTime(float ExposureTimeNum);//设置增益int  setGain(float Gain);//关闭自动曝光int setExposureAuto(bool exposureAutoFlag);//关闭自动增益int setGainAuto(bool gainAutoFlag);//清理相机缓存void clearBuffer();bool QImage2HObject(QImage &qImg, HObject &hImg);private:void*         m_hDevHandle;
public:// 用于保存图像的缓存unsigned int    m_nBufSizeForSaveImage;// 用于从驱动获取图像的缓存unsigned int    m_nBufSizeForDriver;
};#endif // HIKVISIONSDK_H

2.hikvisionsdk.cpp文件(海康相机各功能实现)

在读图相机中图像函数里面有个事件,此时需要找到自己相机的型号才能执行,要不然程序容易闪退

#include "hikvisionsdk.h"MV_CC_DEVICE_INFO_LIST m_stDevList;         // ch:设备信息列表结构体变量,用来存储设备列表
MV_CC_DEVICE_INFO* m_Device=NULL;                 //设备对象#include <QDebug>
#include <QString>
#include <QDebug>
HikvisionSDK::HikvisionSDK()
{m_hDevHandle    = NULL;
}HikvisionSDK::~HikvisionSDK()
{if (m_hDevHandle){MV_CC_DestroyHandle(m_hDevHandle);m_hDevHandle    = NULL;}
}//连接相机
int  HikvisionSDK::connectCamera(int id)
{// Enum deviceMV_CC_DEVICE_INFO_LIST stDeviceList;memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);if (nRet!=0){qDebug()<<"nRet:="<<nRet;return -1;}if (stDeviceList.nDeviceNum > 0){for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++){MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];if (NULL == pDeviceInfo){break;}}}else{return -1;}unsigned int nIndex = 0;// Select device and create handlenRet = MV_CC_CreateHandle(&m_hDevHandle, stDeviceList.pDeviceInfo[nIndex]);if (nRet!=0){//printf("Create Handle fail! nRet [0x%x]\n", nRet);return -1;}// open devicenRet = MV_CC_OpenDevice(m_hDevHandle);if (nRet!=0){//printf("Open Device fail! nRet [0x%x]\n", nRet);return -1;}return 0;
}//启动相机采集
int HikvisionSDK::startCamera()
{int tempValue=MV_CC_StartGrabbing(m_hDevHandle);if(tempValue!=0){return -1;}else{return 0;}
}//停止相机采集
int HikvisionSDK::stopCamera()
{int tempValue=MV_CC_StopGrabbing(m_hDevHandle);if(tempValue!=0){return -1;}else{return 0;}
}//关闭相机
int HikvisionSDK::closeCamera()
{if (NULL == m_hDevHandle){return -1;}MV_CC_CloseDevice(m_hDevHandle);int tempValue = MV_CC_DestroyHandle(m_hDevHandle);m_hDevHandle = NULL;if(tempValue!=0){return -1;}else{return 0;}
}//发送软触发
int HikvisionSDK::softTrigger()
{int tempValue= MV_CC_SetCommandValue(m_hDevHandle, "TriggerSoftware");if(tempValue!=0){return -1;}else{return 0;}
}//读取相机中的图像
int HikvisionSDK::ReadBuffer(HObject &halconImage)
{//Mat* getImage=new Mat();unsigned int nRecvBufSize = 0;MVCC_INTVALUE stParam;qDebug()<<"enter3";memset(&stParam, 0, sizeof(MVCC_INTVALUE));int tempValue = MV_CC_GetIntValue(m_hDevHandle, "PayloadSize", &stParam);qDebug()<<"enter4";if (tempValue != 0){return -1;}//分配一个指针的内存大小 ,  c语言的库函数  malloc;nRecvBufSize = stParam.nCurValue;//指针  图像接收数据的指针unsigned char* pDate;pDate=(unsigned char *)malloc(nRecvBufSize);qDebug()<<"enter41";//句柄  指针   nRecvBufSize  700 ,  输出  (指针和图像信息的结构体)stImageInfo。MV_FRAME_OUT_INFO_EX stImageInfo = {0};tempValue= MV_CC_GetOneFrameTimeout(m_hDevHandle, pDate, nRecvBufSize, &stImageInfo, 700);if(tempValue!=0){return -1;}
//    m_nBufSizeForSaveImage = stImageInfo.nWidth * stImageInfo.nHeight * 3 + 2048;
//    unsigned char* m_pBufForSaveImage;
//    m_pBufForSaveImage = (unsigned char*)malloc(m_nBufSizeForSaveImage);qDebug()<<"enter42";bool isMono;//HObject halconImage;//stImageInfo.enPixelType//像素格式  的  stImageInfo  ,switch  调整,switch (stImageInfo.enPixelType){case PixelType_Gvsp_Mono8:case PixelType_Gvsp_Mono10:case PixelType_Gvsp_Mono10_Packed:case PixelType_Gvsp_Mono12:case PixelType_Gvsp_Mono12_Packed:case      17301512:isMono=true;break;default:isMono=false;break;}if(isMono){//*getImage=Mat(stImageInfo.nHeight,stImageInfo.nWidth,CV_8UC1,pDate);//imwrite("d:\\测试opencv_Mono.tif", image);qDebug()<<" stImageInfo.nHeight:"<< stImageInfo.nHeight;qDebug()<<" stImageInfo.nWidth:"<< stImageInfo.nWidth;qDebug()<<"pDate:"<<pDate;GenImage1(&halconImage, "byte", stImageInfo.nWidth,stImageInfo.nHeight,(Hlong)(pDate));//转换为hoject
//        GenImage1(&hImg, "byte", tFrameInfo.iWidth, tFrameInfo.iHeight, (Hlong)m_pchImgBuffer[camId]);
//         qDebug()<<"pDate11:"<<pDate;//WriteImage(halconImage, "png", 0, "./picture/halcon_Mono.png");}else{//转换图像格式为BGR8MV_CC_PIXEL_CONVERT_PARAM stConvertParam = {0};memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));stConvertParam.nWidth = stImageInfo.nWidth;                 //ch:图像宽 | en:image widthstConvertParam.nHeight = stImageInfo.nHeight;               //ch:图像高 | en:image height//stConvertParam.pSrcData = m_pBufForDriver;                  //ch:输入数据缓存 | en:input data bufferstConvertParam.pSrcData = pDate;                  //ch:输入数据缓存 | en:input data bufferstConvertParam.nSrcDataLen = stImageInfo.nFrameLen;         //ch:输入数据大小 | en:input data sizestConvertParam.enSrcPixelType = stImageInfo.enPixelType;    //ch:输入像素格式 | en:input pixel formatstConvertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed; //ch:输出像素格式 | en:output pixel format  适用于OPENCV的图像格式//stConvertParam.enDstPixelType = PixelType_Gvsp_RGB8_Packed; //ch:输出像素格式 | en:output pixel format//stConvertParam.pDstBuffer = m_pBufForSaveImage;                    //ch:输出数据缓存 | en:output data bufferstConvertParam.nDstBufferSize = m_nBufSizeForSaveImage;            //ch:输出缓存大小 | en:output buffer sizeMV_CC_ConvertPixelType(m_hDevHandle, &stConvertParam);QImage img = QImage((const uchar*)pDate,  stImageInfo.nWidth,stImageInfo.nHeight, QImage::Format_RGB888);QImage2HObject(img, halconImage);//QImage2HObject(img, hImg);//halconImage  输出  rgb   hobject 格式的图像 。//因为 这里没有 图片 输出  所以 代码就奔溃了。//*getImage=Mat(stImageInfo.nHeight,stImageInfo.nWidth,CV_8UC3,m_pBufForSaveImage);//imwrite("d:\\测试opencv_Color.tif", image);}//CopyImage(halconImage, &image);free(pDate);//free(m_pBufForSaveImage);return 0;
}//Qimage  转换成 hobject image
bool HikvisionSDK::QImage2HObject(QImage &qImg, HObject &hImg)
{try{if (qImg.isNull()) return false;int width = qImg.width();int height = qImg.height();QImage::Format format = qImg.format();if (format == QImage::Format_RGB32 ||format == QImage::Format_ARGB32 ||format == QImage::Format_ARGB32_Premultiplied){GenImageInterleaved(&hImg, Hlong(qImg.bits()), "bgrx", width, height, 0, "byte", width, height, 0, 0, 8, 0);}else if (format == QImage::Format_RGB888){GenImageInterleaved(&hImg, Hlong(qImg.bits()), "bgr", width, height, 0, "byte", width, height, 0, 0, 8, 0);}else if (format == QImage::Format_Grayscale8 || format == QImage::Format_Indexed8){GenImage1Extern(&hImg, "byte", width, height, Hlong(qImg.bits()), NULL);}}catch (const std::exception&){return false;}return true;
}//获取图像高度值
int HikvisionSDK::getHeight()
{MVCC_INTVALUE stParam;memset(&stParam, 0, sizeof(MVCC_INTVALUE));int tempValue=MV_CC_GetIntValue(m_hDevHandle, "Height", &stParam);int value= stParam.nCurValue;if(tempValue!=0){return -1;}else{return value;}
}//获取图像宽度值
int HikvisionSDK::getWidth()
{MVCC_INTVALUE stParam;memset(&stParam, 0, sizeof(MVCC_INTVALUE));int tempValue=MV_CC_GetIntValue(m_hDevHandle, "Width", &stParam);int value= stParam.nCurValue;if(tempValue!=0){return -1;}else{return value;}
}//获取相机曝光时间
float HikvisionSDK::getExposureTime()
{MVCC_FLOATVALUE stParam;memset(&stParam, 0, sizeof(MVCC_INTVALUE));int tempValue=MV_CC_GetFloatValue(m_hDevHandle, "ExposureTime", &stParam);float value= stParam.fCurValue;if(tempValue!=0){return -1;}else{return value;}
}//设置图像ROI高度
int HikvisionSDK::setHeight(unsigned int height)
{int tempValue=MV_CC_SetIntValue(m_hDevHandle, "Height", height);if(tempValue!=0){return -1;}else{return 0;}
}//设置图像ROI宽度
int HikvisionSDK::setWidth(unsigned int width)
{int tempValue=MV_CC_SetIntValue(m_hDevHandle, "Width", width);if(tempValue!=0){return -1;}else{return 0;}
}//设置图像水平偏移OffsetX
int HikvisionSDK::setOffsetX(unsigned int offsetX)
{int tempValue=MV_CC_SetIntValue(m_hDevHandle, "OffsetX", offsetX);if(tempValue!=0){return -1;}else{return 0;}
}//设置图像竖直偏移OffsetY
int HikvisionSDK::setOffsetY(unsigned int offsetY)
{int tempValue=MV_CC_SetIntValue(m_hDevHandle, "OffsetY", offsetY);if(tempValue!=0){return -1;}else{return 0;}
}//设置是否为触发模式
int HikvisionSDK::setTriggerMode(unsigned int TriggerModeNum)
{//0:Off  1:Onint tempValue= MV_CC_SetEnumValue(m_hDevHandle,"TriggerMode", TriggerModeNum);if(tempValue!=0){return -1;}else{return 0;}
}//设置触发源
int HikvisionSDK::setTriggerSource(unsigned int TriggerSourceNum)
{//0:Line0  1:Line1  7:Softwareint tempValue= MV_CC_SetEnumValue(m_hDevHandle,"TriggerSource", TriggerSourceNum);if(tempValue!=0){return -1;}else{return 0;}
}//设置帧率控制使能
int HikvisionSDK::setFrameRateEnable(bool comm)
{int tempValue =MV_CC_SetBoolValue(m_hDevHandle, "AcquisitionFrameRateEnable", comm);if (tempValue != 0){return -1;}else{return 0;}
}//设置心跳时间
int HikvisionSDK::setHeartBeatTime(unsigned int time)
{//心跳时间最小为500msif(time<500)time=500;int tempValue=MV_CC_SetIntValue(m_hDevHandle, "GevHeartbeatTimeout", time);if(tempValue!=0){return -1;}else{return 0;}
}//设置曝光时间
int HikvisionSDK::setExposureTime(float ExposureTimeNum)
{int tempValue= MV_CC_SetFloatValue(m_hDevHandle, "ExposureTime",ExposureTimeNum );if(tempValue!=0){return -1;}else{return 0;}
}//设置曝光时间
int HikvisionSDK::setGain(float Gain)
{int tempValue= MV_CC_SetFloatValue(m_hDevHandle, "Gain",Gain);if(tempValue!=0){return -1;}else{return 0;}
}//关闭自动曝光
int HikvisionSDK::setExposureAuto(bool exposureAutoFlag)
{int tempValue= MV_CC_SetEnumValue(m_hDevHandle,"ExposureAuto", exposureAutoFlag);if (tempValue != 0){return -1;}else{return 0;}
}//关闭自动增益
int HikvisionSDK::setGainAuto(bool gainAutoFlag)
{int tempValue= MV_CC_SetEnumValue(m_hDevHandle,"GainAuto", gainAutoFlag);if (tempValue != 0){return -1;}else{return 0;}
}//清理相机缓存
void HikvisionSDK::clearBuffer()
{//stopCamera();//startCamera();
}

4.在主程序相机实例化

1.首先包含公共相机和海康相机的头文件

2.用公共相机接口去实例化海康相机

.h主文件

private: 
//相机指针CameraInterface * camera=NULL;

.cpp主文件

  camera=new HikvisionSDK();用公共相机接口实例化海康相机类

析构函数中

if(camera!=NULL){delete camera;camera=NULL;}

5.连接相机

1.在IU界面下添加连接相机按钮转到槽函数

//连接相机
void MainWindow::on_btn_Connect_clicked()
{//初始化相机句柄if(camera!=NULL){delete camera;camera=NULL;}qDebug()<<111;if(camera==NULL){qDebug()<<112;InitCam();//连接相机函数//控制按钮是否可以按下ui->btn_Close->setEnabled(false);ui->btn_Connect->setEnabled(false);ui->btn_Trigger->setEnabled(true);ui->lxcj->setEnabled(true);ui->StopAcquisition->setEnabled(true);}}

2.连接相机函数实现

注意:次函数是实例化具体的相机(因为在项目中可能用到不同类型的相机,所以在头文件首先用公共相机接口)然后在具体的连接相机下面去实例化具体的相机类型对象。

  最后一行程序,是把这个具体的海康相机对象通过传参,传入到线程类中去。

void MainWindow::InitCam()
{camera=new HikvisionSDK();此时用到了上面提到的海康相机实例化//连接相机//std::cout<<"Connect:  "<<camera->connectCamera(1)<<std::endl;int  ret=camera->connectCamera(1);if(ret!=0){//失败//QMessageBox::warning()}//设置为触发模式  打开std::cout<<"TriggerMode:  "<<camera->setTriggerMode(1)<<std::endl;//设置触发源为软触发std::cout<<"TriggerSource:  "<<camera->setTriggerSource(7)<<std::endl;//设置曝光时间std::cout<<"SetExposureTime:  "<<camera->setExposureTime(40000)<<std::endl;//开启相机采集std::cout<<"StartCamera:  "<<camera->startCamera()<<std::endl;myThread->getCameraPtr(camera);//把实例化后的海康相机类传入到线程中去//myThread->getImagePtr(myImage);}

6.定义线程类(mythread)

1.在线程里面需要包含公共相机的头文件

#include "QThread"//线程
#include "camerainterface.h"//公共相机接口
#include <QImage>

2.头文件定义接收海康相机类的函数

    void getCameraPtr(CameraInterface* camera);

3..cpp文件具体实现

//析构函数
MyThread::~MyThread()
{if(cameraPtr==NULL){delete cameraPtr;}
}//具体实现
void MyThread::getCameraPtr(CameraInterface *camera)
{cameraPtr=camera;
}

7.单次采集

1.在IU界面添加单次采集按钮转跳槽函数

2.需要在主头文件中,private: 下面去声明 bool IsRun = false;(目的是为了更好的调节相机采图启停,方便后续操作(在这里可以不用))

3.转到线程中,去执行停止线程工作内容(因为单次采集不需要启动线程)

//单次采集
void MainWindow::on_btn_Trigger_clicked()
{HObject image;HTuple hv_Width,hv_Height;IsRun =true;//单次采图状态myThread->ChangeCloseStatus();//线程状态启停std::cout<<"TriggerMode:  "<<camera->setTriggerMode(1)<<std::endl;std::cout<<"SoftTrigger:  "<<camera->softTrigger()<<std::endl;//读取Mat格式的图像qDebug()<<"enter1";camera->ReadBuffer(image);   //相机类里面的读图函数CopyImage(image, &halconImage);if(ui->checkBox_saveImage->isChecked()){//保存图像QString path="./data/"+QString::number(ui->sB_Num->value())+".png";WriteImage(halconImage, "png", 0, HTuple(path.toLatin1().data()));}GetImageSize(image, &hv_Width, &hv_Height);SetPart(WindowHandle, 0, 0, hv_Height,hv_Width); // 1944 2592qDebug()<<"enter2";DispObj(halconImage, WindowHandle);//其它图像处理功能//m_pMeasure01->OutMeasure01(halconImage);
}

4.在线程类中,.h的public:下定义 bool Status=false;

同时定义线程状态启停函数

    void ChangeCloseStatus();//关闭void ChangeOpenStatus();//打开.cpp文件void MyThread::ChangeCloseStatus()
{Status=false;//qDebug()<<"Status:="<<Status;
}void MyThread::ChangeOpenStatus()
{Status=true;//qDebug()<<"Status:="<<Status;
}


8.连续采集

1.此时需要利用线程进行连续图像采集,需要在主程序中实例化线程类(应该在一开始就实例化)

    //线程对象MyThread* myThread=NULL;
   //  线程对象实例化myThread = new MyThread();
    if(myThread!=NULL){delete myThread;myThread=NULL;}

2.在IU界面上添加连续采集按钮转到槽函数

void MainWindow::on_lxcj_clicked()
{std::cout<<"TriggerMode:  "<<camera->setTriggerMode(0)<<std::endl;myThread->ChangeOpenStatus();//打开线程工作内容if(!IsRun)//判断单次采集是否打开{IsRun = false;关闭单次采集myThread->start();//开启连续彩图线程}else{IsRun = false;//关闭单次采集myThread->start();//开启连续彩图线程}
}

3.在线程类里面

.h文件

    void run();//定义run函数(线程里的工作内容)void display( HObject imagePtr);//(用来其它作用显示)signals:void CaptureImage(HObject);//定义一个发送图像的信号

.cpp文件

void MyThread::run()
{if(cameraPtr==NULL){return;}while(!isInterruptionRequested()){if(Status)  //判断是否停止的状态{qDebug()<<"thread current" <<currentThread();std::cout<<"Thread_Trigger:"<<cameraPtr->softTrigger()<<std::endl;HObject image;//读取Mat格式的图像cameraPtr->ReadBuffer(image);emit CaptureImage(image);//发送图像信号msleep(100);}}
}
void MyThread::display( HObject imagePtr)
{//qDebug()<<"Enter2";//std::cout<<"so"<<std::endl;//判断是黑白、彩色图像DispObj((imagePtr), WindowHandle);QString path="./picture/halcon"+QString::number(1)+".png";//WriteImage((*imagePtr), "png", 0, HTuple(path.toLatin1().data()));//int num=ui->sB_Num->value();//ui->sB_Num->setValue(num+1);
}

4.在线程中发送图像信号之后,需要在主函数中去接收这个图像信号

 //信号与槽connect(myThread,SIGNAL(CaptureImage(HObject)),this,SLOT(ImageProcess01(HObject)),\Qt::BlockingQueuedConnection);//信号槽函数   。必须display  是slot
private slots://显示连续图像,接收槽函数void ImageProcess01(HObject image); //信号槽函数  --收到图像之后的  图像处理槽函数

//槽函数接收显示图像
void MainWindow::ImageProcess01(HObject halconImage)
{HTuple hv_Width,hv_Height;qDebug()<<"Enter1";GetImageSize(halconImage, &hv_Width, &hv_Height);SetPart(WindowHandle, 0, 0, hv_Height,hv_Width); // 1944 2592display(halconImage);m_pMeasure01->OutMeasure01(halconImage);  //输出一个宽度//tuple -length  ,length ==1   识别 成功//TCP  服务器 发给客户端  ,一个字符串。myThread->ChangeOpenStatus();//采图   收到图像处理完的信号,可以启动继续采图。同步采集!qDebug()<<"Enter2";}

9.停止采集

void MainWindow::on_StopAcquisition_clicked()
{myThread->ChangeCloseStatus();//停止线程状态ui->btn_Close->setEnabled(true);if(IsRun){myThread->requestInterruption();myThread->wait();//线程等待IsRun =false;//单次采集关闭// camera->stopCamera();// camera->closeCamera();}
}

10.关闭相机

void MainWindow::on_btn_Close_clicked()
{ui->btn_Trigger->setEnabled(false);ui->lxcj->setEnabled(false);ui->StopAcquisition->setEnabled(false);if(camera!=NULL){myThread->requestInterruption();camera->closeCamera();}//ui->Image_Label->clear();ui->btn_Connect->setEnabled(true);}

11.保存图像

void MainWindow::on_saveImage_clicked()
{QString filePath ="./data/picture/"+ QDateTime::currentDateTime().toString("yyyy-MM-dd");HTuple file1=HTuple(filePath.toStdString().c_str());WriteImage(halconImage, "bmp", 0, file1);//ui->textBrowser_log->append("write image success.");
}

12.设置曝光、增益

//曝光
void MainWindow::on_pB_exposTime_clicked()
{int exposTime=ui->sB_exposure->value();int ret=camera->setExposureTime(exposTime);qDebug()<<"ret:="<<ret;
}//增益
void MainWindow::on_pB_setGain_clicked()
{int Gain =ui->sB_Gain->value();int ret=camera->setGain(Gain);qDebug()<<"ret:="<<ret;
}

13.保存参数

//把界面的参数  固化 到 本地文件
void Measure01::on_pB_saveParameters_clicked()
{/*AmplitudeThreshold = 23;RoiWidthLen = 67;Alpha1 = 3.4;LineRowStart = 879.281;LineColumnStart = 1436.34;LineRowEnd = 1769.58;LineColumnEnd = 3055.55;*///把界面的参数 赋值 给局部作用域的变量MeasureParam01.AmplitudeThreshold=ui->SpinBox_AmpThre->value();MeasureParam01.Alpha1=ui->SpinBox_Alpha1->value();MeasureParam01.RoiWidthLen=ui->SpinBox_RoiWidthLen->value();MeasureParam01.LineRowStart=ui->SpinBox_LineRowStart->value();MeasureParam01.LineColumnStart=ui->SpinBox_LineColumnStart->value();MeasureParam01.LineRowEnd=ui->SpinBox_LineRowEnd->value();MeasureParam01.LineColumnEnd=ui->SpinBox_LineColumnEnd->value();//再通过qsetting的方法 把参数保存到qt 本地的 文件中去SaveSetting(CONFIG_FILEPATH,"Measure01","AmplitudeThreshold",\QVariant(MeasureParam01.AmplitudeThreshold));//AmpThreSaveSetting(CONFIG_FILEPATH,"Measure01","Alpha1",\QVariant(MeasureParam01.Alpha1));//AmpThreSaveSetting(CONFIG_FILEPATH,"Measure01","RoiWidthLen",\QVariant(MeasureParam01.RoiWidthLen));//AmpThreSaveSetting(CONFIG_FILEPATH,"Measure01","LineRowStart",\QVariant(MeasureParam01.LineRowStart));//AmpThreSaveSetting(CONFIG_FILEPATH,"Measure01","LineColumnStart",\QVariant(MeasureParam01.LineColumnStart));//AmpThreSaveSetting(CONFIG_FILEPATH,"Measure01","LineRowEnd",\QVariant(MeasureParam01.LineRowEnd));//AmpThreSaveSetting(CONFIG_FILEPATH,"Measure01","LineColumnEnd",\QVariant(MeasureParam01.LineColumnEnd));//AmpThre}

14.加载参数

构造函数里面

 // //把界面的参数 赋值 给局部作用域的变量//double AmpThre=ui->SpinBox_AmpThre->value();//再通过qsetting的方法 把参数保存到qt 本地的 文件中去//SaveSetting(CONFIG_FILEPATH,"Measure01","AmplitudeThreshold",QVariant(AmpThre));//AmpThreQVariant ValueAmpThre;QVariant ValueAlpha1;QVariant ValueRoiWidthLen;QVariant ValueRowStart;QVariant ValueColumnStart;QVariant ValueRowEnd;QVariant ValueColumnEnd;LoadSetting(CONFIG_FILEPATH,"Measure01","AmplitudeThreshold",ValueAmpThre);LoadSetting(CONFIG_FILEPATH,"Measure01","Alpha1",ValueAlpha1);//AmpThreLoadSetting(CONFIG_FILEPATH,"Measure01","RoiWidthLen",ValueRoiWidthLen);//AmpThreLoadSetting(CONFIG_FILEPATH,"Measure01","LineRowStart",ValueRowStart);//AmpThreLoadSetting(CONFIG_FILEPATH,"Measure01","LineColumnStart",ValueColumnStart);//AmpThreLoadSetting(CONFIG_FILEPATH,"Measure01","LineRowEnd",ValueRowEnd);//AmpThreLoadSetting(CONFIG_FILEPATH,"Measure01","LineColumnEnd",ValueColumnEnd);//AmpThre//把QVariant 类型 转换 double 类型MeasureParam01.AmplitudeThreshold=ValueAmpThre.toDouble();MeasureParam01.Alpha1=ValueAlpha1.toDouble();MeasureParam01.RoiWidthLen=ValueRoiWidthLen.toDouble();MeasureParam01.LineRowStart=ValueRowStart.toDouble();MeasureParam01.LineColumnStart=ValueColumnStart.toDouble();MeasureParam01.LineRowEnd=ValueRowEnd.toDouble();MeasureParam01.LineColumnEnd=ValueColumnEnd.toDouble();ui->SpinBox_AmpThre->setValue(MeasureParam01.AmplitudeThreshold);ui->SpinBox_Alpha1->setValue(MeasureParam01.Alpha1);ui->SpinBox_RoiWidthLen->setValue(MeasureParam01.RoiWidthLen);ui->SpinBox_LineRowStart->setValue(MeasureParam01.LineRowStart);ui->SpinBox_LineColumnStart->setValue(MeasureParam01.LineColumnStart);ui->SpinBox_LineRowEnd->setValue(MeasureParam01.LineRowEnd);ui->SpinBox_LineColumnEnd->setValue(MeasureParam01.LineColumnEnd);ui->SpinBox_AmpThre->setStyleSheet("background-color: green");

15.日志类(lxlog)

.h

#ifndef __LX_LOG_H__
#define __LX_LOG_H__#include<QString>
#include <QTextBrowser>void WriteLog(QString LogType, QString str);
void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);#endif

.cpp

#include "lxlog.h"
#include "paramsconfig.h"
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QTextBrowser>extern QTextBrowser * g_pTb;void WriteLog(QString LogType, QString str)
{//文件名QString filePath = QString(LOG_PATH) + '/'+ QDateTime::currentDateTime().toString("yyyy-MM-dd")+".log";//时间QString strToWrite = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");//类型strToWrite.append(QString(" %1 ").arg(LogType));//信息strToWrite.append(QString("%1").arg(str));QFile file(filePath);file.open(QIODevice::WriteOnly | QIODevice::Append);QTextStream text_stream(&file);text_stream << strToWrite << "\r\n";file.flush();file.close();g_pTb->append(strToWrite);
}void MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{QString txtMessage = "";QString messageType = "";switch (type) {case QtDebugMsg:    //调试信息提示messageType = "Debug";txtMessage = QString("Debug: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);break;case QtInfoMsg:messageType = "Info";txtMessage = QString("Warning: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);break;case QtWarningMsg:    //一般的warning提示messageType = "Waring";txtMessage = QString("Warning: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);break;case QtCriticalMsg:    //严重错误提示messageType = "Critical";txtMessage = QString("Critical: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);//PostErrorMessage(txtMessage, messageType);break;case QtFatalMsg:    //致命错误提示messageType = "Fatal";txtMessage = QString("Fatal: %1 (%2:%3, %4)\n").arg(msg).arg(context.file).arg(QString::number(context.line)).arg(context.function);abort();}WriteLog(txtMessage, messageType);
}


16.公共参数、结构体类(paramsconfig)

.h

#ifndef PARAMSCONFIG_H
#define PARAMSCONFIG_H#include <QString>
#include <QVariant>#define SYS_CONFIG_FILE				"./data/sysConfig/params.ini"
#define USER_CONFIG_PATH			"./data/userConfig"
#define LOG_PATH					"./data/log"
#define TRAIN_FILEPATH              "./data/train"
#define CALIB_DATA_PATH             "data/calib"
#define IMG_DEPETH_FILEPATH         "data/imageDepth"
#define IMG_RAW_FILEPATH            "data/imageRaw"
#define MODEL_FILEPATH              "data/model"
#define SENCE_FILEPATH              "data/sence"
#define CONFIG_FILEPATH             "data/config/params.ini"
#define CONFIG_Main_FILEPAT         "data/config/my.ini"
#define CONFIG_Face_FILEPAT         "data/config/Face.ini"
#define CONFIG_Box_FILEPAT          "data/config/Box.ini"
#define CONFIG_CreatModel_FILEPAT   "data/config/CreatModel.ini"#define POSE_TO_HTUPLE(pose, hTuple) {hTuple[0] = pose.x; hTuple[1] = pose.y; hTuple[2] = pose.z; hTuple[3] = pose.rx; hTuple[4] = pose.ry; hTuple[5] = pose.rz; hTuple[6] = 0.0;}
#define HTUPLE_TO_POSE(hTuple, pose) {pose.x = hTuple[0]; pose.y = hTuple[1]; pose.z = hTuple[2]; pose.rx = hTuple[3]; pose.ry = hTuple[4]; pose.rz = hTuple[5];}#define KD_PI 3.141592653
#define LENGTH_UNIT "mm"
#define LX_STATIONS_NUM    3       // 相机数量#include "HalconCpp.h"
using namespace HalconCpp;/*** measure*/
typedef struct
{double AmplitudeThreshold;double RoiWidthLen;double Alpha1;double LineRowStart;double LineColumnStart;double LineRowEnd;double LineColumnEnd;
}MeasureParam;/*** pose*/
typedef struct {double x;                               ///X偏移double y;                               ///Y偏移double z;                               ///Z偏移double rx;                              ///X旋转double ry;                              ///Y旋转double rz;                              ///Z旋转int type;                               ///姿态类型
} KD_POSE;/*** point select*/
typedef struct {double xMin;                            ///X范围double xMax;                            ///X范围double yMin;                            ///Y范围double yMax;                            ///Y范围double zMin;                            ///Z范围double zMax;                            ///Z范围int type;                               ///备用
} KD_POINT_SELECT;/*** 机械臂工具类型*/
typedef enum {ROT_TOOL_SUCKER = 0,                    ///吸盘ROT_POSE_GRIPPER_PNEUMATIC,             ///夹具气动ROT_POSE_GRIPPER_ELECTRIC,              ///夹具电动
}ROT_TOOL_TYPE;/*** 三维点云控制参数*/
typedef struct {KD_POSE calibPose;                      ///校正PoseKD_POINT_SELECT potSelc;                ///范围限定
} KD_3D_CTRL;                               ///三维控制参数/*** 三维物体参数*/
typedef struct {KD_POSE * pObjChangePose;               ///偏移poseunsigned int surfaceNum;                ///面数double objGuideHigh;                    ///物体抓取引导高度
} KD_OBJECT_CTRL;                           ///三维控制参数/*** 3d匹配参数*/
typedef struct {double objectMaxLength;                 ///物体最大长度double sampleDis;                       ///采样距离double keyPotFraction;                  ///特征点最小得分double minScore;                        ///最小得分QString * modelFileName;                ///模型文件名KD_POSE toolInFlangeCenter;             ///工具在法兰盘中心(由机械臂标定得出)KD_POSE camInBase;                      ///相机在基础 (由halcon手眼标定得出)KD_POSE toolInObj;                      ///工具在物体 (由halcon手眼标定得出)KD_3D_CTRL bgInfo;                      ///背景抽取参数
} KD_SURFACE_MATCH_CTRL;/*** 机械臂参数*/
typedef struct {int speed;                              ///速度(测试用)double jointVelc;                       ///角速度double jointAcc;                        ///角加速度double lineVelc;                        ///线速度double lineAcc;                         ///线加速度int colliClass;                         ///防撞等级KD_POSE toolInFlangeCenter;             ///工具在法兰盘中心
} KD_ROBOT_CTRL;/*** 机械臂码垛参数*/
typedef struct {KD_POSE oriPose;                        ///起始码垛姿态KD_POSE offsetPose;                     ///偏移码垛姿态int palletNum;                          ///码垛数量double palletSize;                      ///工件尺寸
} KD_ROBOT_PALLET_CTRL;/*** 机械臂运动参数*/
typedef struct {double doorHigh;                        ///门型高度int ioId;                               ///吸盘、夹具IO口int moveTime;                           ///机械臂从抓取到放置位置延时时间int doorUpDownTime;                     ///门型上下移动延时时间int suctionTime;                        ///吸盘、夹具延时时间KD_POSE halfWayPose;                    ///中途路点KD_ROBOT_PALLET_CTRL pallet;            ///码垛参数ROT_TOOL_TYPE toolType;                 ///工具类型
} KD_ROBOT_MOVE_CTRL;typedef enum {UNINIT,				//还未初始化RUNNING,			//运行STOP				//停止
}PROGRAM_STATUS;/**
* DO输出0
*/
typedef enum {KEY_RELEASE = 0,KEY1 = 1,KEY2 = 2,KEY3 = 4,KEYS = 8,KEYU = 16,KEYD = 32
}HC_KEY_TYPE;/**
* DO输出1
*/
typedef enum {DO_NONE	   = 0,DO_GUIDE1  = 1,	// 推进DO_GUIDE2  = 2, // 推出DO_BUZZER  = 4,DO_LIGHT   = 8,DO_BACKUP1 = 16,DO_BACKUP2 = 32
}HC_DO_TYPE;/**
* DI输入
*/
typedef enum {DI_GUIDE1 = 1,	// 推进状态DI_GUIDE2 = 2,  // 推出状态DI_START  = 4,DI_RESET  = 8,DI_STOP   = 16,DI_BACKUP = 32
}HC_DI_TYPE;/*** 矩形*/
typedef struct {int x;int y;int w;int h;
} LX_RECT;/*** 识别参数*/
typedef struct {LX_RECT roiRect;                    ///ROI区域int erosionVal;                     ///腐蚀参数int dilationVal;                    ///膨胀参数int noiseArea;                      ///噪声面积(小于该面积都被认为是噪声)int decimalPointArea;				///小数点面积double confidence;					///最低置信率QString trainFile;					///训练文件QString usrconfigFile;				///测试项目配置文件
} LX_RECOGNISE_CTRL;//LX_RECOGNISE_CTRL LX_RECOGNISE_CTRL1;
//LX_RECOGNISE_CTRL LX_RECOGNISE_CTRL2;
/*** 相机参数*/
typedef struct {int exposureTime;int exposureTimeMin;int exposureTimeMax;int gain;int gainMin;int gainMax;
//    bool bMiroH;
//    bool bMiroV;
} LX_CAMERA_CTRL;/*** io*/
typedef struct {QString key1Do;									//io口IDQString key2Do;									//io口IDQString key3Do;									//io口IDQString keySDo;									//io口IDQString keyUpDo;								//io口IDQString keyDownDo;								//io口IDQString keyBuzzerDo;                            //io口IDQString keyStartDi;								//io口IDQString keyResetDi;								//io口IDQString keyStopDi;								//io口ID
} LX_IO_CTRL;/*** 初始化参数*/
typedef struct {int pressTime;                      ///按下时间QString defaultVal;                 ///初始值
} HC_INIT_CTRL;/*** 公英制参数*/
typedef struct {int unit;							///0为公制值, 1为英制
} HC_METRIC_IMPERIAL_CTRL;/*** 数字缺失*/
typedef struct {QString defaultVal;					///预设值
} HC_NUM_MISS_CTRL;/*** 记忆位*/
typedef struct {int pressTime;                     ///单次上升时间
} HC_MEMORY_POS_CTRL;/*** 陀螺仪*/
typedef struct {QString defaultVal;                ///陀螺仪预设等级
} HC_GYRO_CTRL;/*** 上升下降锁*/
typedef struct {int pressTime;                      ///按下时间
} HC_LOCK_CTRL;/*** 异常码*/
//typedef struct {
//    int pressTimeE05;                   ///E05按下时间
//}HC_EXCEPTION_CTRL;/*** 手控器检测和预设参数*/
typedef struct {HC_INIT_CTRL initCtrl;HC_METRIC_IMPERIAL_CTRL metricImperialCtrl;HC_NUM_MISS_CTRL numMisCtrl;HC_MEMORY_POS_CTRL memoryPosCtrl;HC_GYRO_CTRL gyroCtrl;HC_LOCK_CTRL lockCtrl;int testItem;														//测试项int workStationId;
} LX_HC_CTRL;/***2d模板匹配的预设参数*/typedef  struct {double OffsetRow;double OffsetColumn;double TPhi;double TLength1;double TLength2;int m_nModecase;   // =1HTuple ModelID;HObject ShapeModel;
} LX_MATCH_CTRL;//保存加载配置
void SaveSetting(QString fileName, QString group, QString key, QVariant value);
void LoadSetting(QString fileName, QString group, QString key, QVariant &value);//保存加载识别配置
void SaveRecogniseCtrl(QString fileName, QString group, LX_RECOGNISE_CTRL recCtrl);
void LoadRecogniseCtrl(QString fileName, QString group, LX_RECOGNISE_CTRL &recCtrl);//保存加载IO配置
void SaveIoCtrl(QString fileName, QString group, LX_IO_CTRL ioCtrl);
void LoadIoCtrl(QString fileName, QString group, LX_IO_CTRL &ioCtrl);//保存加载相机配置
void SaveCameraCtrl(QString fileName, QString group, LX_CAMERA_CTRL camCtrl);
void LoadCameraCtrl(QString fileName, QString group, LX_CAMERA_CTRL &camCtrl);//保存加载手控器配置
void SaveHandCtrlerCtrl(QString fileName, QString group, LX_HC_CTRL hcCtrl);
void LoadHandCtrlerCtrl(QString fileName, QString group, LX_HC_CTRL &hcCtrl);//保存相机的 产品唯一序列号和工位
void  SaveCameraAscn(QString fileName, QString Key,QString  CameraNumber );
void  LoadCameraAscn(QString fileName, QString Key,QString  &CameraNumber );double AngleToRad(double angle);
double RadToAngle(double rad);//保存pose
void SavePose(QString group, KD_POSE pose);//加载pose
void LoadPose(QString group, KD_POSE &pose);class paramsConfig
{
public:paramsConfig();
};#endif // PARAMSCONFIG_H

.cpp

#include "paramsconfig.h"
#include <QSettings>
#include <QDebug>paramsConfig::paramsConfig()
{}void SaveRecogniseCtrl(QString fileName, QString group, LX_RECOGNISE_CTRL recCtrl)
{}void SaveCameraCtrl(QString fileName, QString group, LX_CAMERA_CTRL camCtrl)
{  // 淇濆瓨鍔犺浇鐩告満閰嶇疆SaveSetting(fileName, group, "exposureTime", QVariant(camCtrl.exposureTime));SaveSetting(fileName, group, "gain", QVariant(camCtrl.gain));SaveSetting(fileName, group, "gainMin", QVariant(camCtrl.gainMin));SaveSetting(fileName, group, "gainMax", QVariant(camCtrl.gainMax));SaveSetting(fileName, group, "exposureTimeMin", QVariant(camCtrl.exposureTimeMin));SaveSetting(fileName, group, "exposureTimeMax", QVariant(camCtrl.exposureTimeMax));
}void LoadCameraCtrl(QString fileName, QString group, LX_CAMERA_CTRL &camCtrl)
{  // 鍔犺浇鐩告満閰嶇疆QVariant qexposureTime;QVariant qexposureTimeMin;QVariant qexposureTimeMax;QVariant qGain;QVariant qGainMin;QVariant qGainMax;LoadSetting(fileName, group, "exposureTime", qexposureTime);LoadSetting(fileName, group, "exposureTimeMin", qexposureTimeMin);LoadSetting(fileName, group, "exposureTimeMax", qexposureTimeMax);LoadSetting(fileName, group, "gain", qGain);LoadSetting(fileName, group, "gainMin", qGainMin);LoadSetting(fileName, group, "gainMax", qGainMax);camCtrl.exposureTime = qexposureTime.toInt();camCtrl.exposureTimeMin = qexposureTimeMin.toInt();camCtrl.exposureTimeMax = qexposureTimeMax.toInt();camCtrl.gain = qGain.toInt();camCtrl.gainMin = qGainMin.toInt();camCtrl.gainMax = qGainMax.toInt();
//    qDebug()<<"camCtrl.gain"<<camCtrl.gain;
//    qDebug()<<"camCtrl.qexposureTime:+"<<camCtrl.exposureTime;
}void SaveHandCtrlerCtrl(QString fileName, QString group, LX_HC_CTRL hcCtrl)
{}void LoadCameraAscn(QString fileName, QString Key, QString &CameraNumber)
{QString group = "CameraManage";QVariant ValueCameraNumber;LoadSetting(fileName,group,Key,ValueCameraNumber);CameraNumber = ValueCameraNumber.toString();qDebug() << "CameraName:= " << CameraNumber;
}void LoadSetting(QString fileName, QString group, QString key, QVariant &value)
{QSettings settings(fileName,QSettings::IniFormat);if(group.size() != 0){settings.beginGroup(group);}value = settings.value(key);if(group.size() != 0){settings.endGroup();}
}void SaveSetting(QString fileName, QString group, QString key, QVariant value)
{QSettings settings(fileName, QSettings::IniFormat);if (group.size() != 0) {settings.beginGroup(group);}settings.setValue(key, value);if (group.size() != 0) {settings.endGroup();}
}void SavePose(QString group, KD_POSE pose)
{}

已经看到这里了,点个赞和关注吧!

      刚开始写文章,如有不足请多多包含;之后会持续更新关于(halcon学习,VS联合编程,QT联合编程,C++,C#,Opencv图像处理库,三维点云库pcl,相机以及机器人的二次开发)等系统化学习文章。


http://www.mrgr.cn/news/100322.html

相关文章:

  • CSS简单实用的加载动画、骨架屏有效果图
  • TP4056 电池管理芯片介绍及电路应用
  • java每日精进 4.26【多租户之过滤器及请求处理流程】
  • 零基础上手Python数据分析 (24):Scikit-learn 机器学习初步 - 让数据预测未来!
  • Goland终端PowerShell命令失效
  • 【Linux网络】构建HTTP响应与请求处理系统 - HttpResponse从理解到实现
  • Kafka 面试,java实战贴
  • JAVA多线程(8.0)
  • 多系统安装经验,移动硬盘,ubuntu grub修改/etc/fstab 移动硬盘需要改成nfts格式才能放steam游戏
  • 【Linux网络】打造初级网络计算器 - 从协议设计到服务实现
  • Deep Reinforcement learning for real autonomous mobile robot navigation
  • Linux下编译并打包MNN项目迁移至其他设备
  • Qt ModbusSlave多线程实践总结
  • AAAI2016论文 UCO: A Unified Cybersecurity Ontology
  • 刚体运动 (位置向量 - 旋转矩阵) 笔记 1.1~1.3 (台大机器人学-林沛群)
  • 云原生--核心组件-容器篇-3-Docker核心之-镜像
  • C++ 同步原语
  • Swift与iOS内存管理机制深度剖析
  • 前端职业发展:如何规划前端工程师的成长路径?
  • 泰迪杯实战案例学习资料:生产线的故障自动识别和人员配置优化