一、前言
在機器視覺、自動化檢測、智能制造等領域,工業相機是獲取圖像數據的核心設備。海康威視作為國內領先的機器視覺廠商,其工業相機產品線豐富,廣泛應用于各類工業場景。
本文將帶你從零開始,使用 海康MVS SDK(Machine Vision Software Development Kit),通過 C/C++語言 實現對海康工業相機的控制,重點演示如何配置 軟件觸發模式 并完成圖像采集。
? 本文特點:全中文輸出日志詳細中文注釋結構清晰,適合初學者基于真實SDK接口編寫
二、開發環境準備
-
硬件準備
海康威視工業相機(GigE / USB3.0 均可)
網線(GigE)或 USB3.0 線(USB)
相機已正確連接并上電 -
軟件準備
操作系統:Linux(Ubuntu/CentOS)或 Windows
安裝 MVS客戶端軟件(含SDK開發包)
開發工具:GCC / G++ / Visual Studio
頭文件:MvCameraControl.h
庫文件:libMvCameraControl.so(Linux)或 MvCameraControl.dll/.lib(Windows)
三、核心功能流程
我們實現的功能流程如下:
初始化SDK → 枚舉設備 → 選擇相機 → 打開設備 → 配置參數 → 啟動取流 → 軟件觸發采集 → 停止取流 → 關閉設備 → 反初始化
本文重點講解 軟件觸發模式 的配置與使用。
四、什么是“軟件觸發”?
工業相機通常有以下幾種工作模式:
模式 說明
連續采集 相機持續輸出圖像
觸發采集 只有收到“觸發信號”才采集一幀
而“軟件觸發”是觸發模式中的一種,指通過 調用SDK函數發送命令 來觸發相機采集一幀圖像,無需外部硬件信號,非常適合調試和控制場景。
🔧 觸發流程:設置 TriggerMode = On設置 TriggerSource = Software調用 MV_CC_SetCommandValue("TriggerSoftware") 發送觸發命令調用 MV_CC_GetImageBuffer() 獲取圖像
五、完整代碼實現(含中文注釋)
以下是完整的 C++ 示例代碼,實現了上述功能。
/*整個流程大概為:
// 1. 先開啟觸發模式
camera.SetEnumValue("TriggerMode", 1); // 1 = On// 2. 設置觸發源為軟件觸發
int mode = 5; // Software
camera.SetEnumValue("TriggerSource", mode);// 3. 發送一次軟件觸發命令
camera.SetCommandValue("TriggerSoftware");// 4. (可選)關閉觸發
// camera.SetEnumValue("TriggerMode", 0); // 0 = Off
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include "MvCameraControl.h"// 全局變量:用于控制程序退出
bool g_bExit = false;// 提示用戶按回車鍵退出程序
void PressEnterToExit(void)
{int c;// 清空輸入緩沖區中的換行符或殘留字符while ((c = getchar()) != '\n' && c != EOF);fprintf(stderr, "\n請按回車鍵退出程序。\n");// 等待用戶真正按下回車while (getchar() != '\n');g_bExit = true; // 設置退出標志sleep(1); // 給線程留出退出時間
}/*** 打印設備基本信息(根據設備類型不同,信息結構不同)* @param pstMVDevInfo: 設備信息結構體指針* @return 成功返回 true,失敗返回 false*/
bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo)
{if (NULL == pstMVDevInfo){printf("錯誤:設備信息指針為空!\n");return false;}// 根據設備傳輸層類型(GigE、USB、CameraLink 等)打印不同信息if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE){// GigE 相機:打印IP、型號、用戶自定義名int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);printf("設備型號名稱: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);printf("當前IP地址: %d.%d.%d.%d\n", nIp1, nIp2, nIp3, nIp4);printf("用戶自定義名稱: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);}else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE){// USB 相機:打印型號和用戶自定義名printf("設備型號名稱: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName);printf("用戶自定義名稱: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);}else if (pstMVDevInfo->nTLayerType == MV_GENTL_GIGE_DEVICE){// GenTL GigE 設備printf("用戶自定義名稱: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);printf("序列號: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chSerialNumber);printf("型號名稱: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);}else if (pstMVDevInfo->nTLayerType == MV_GENTL_CAMERALINK_DEVICE){// CameraLink 設備printf("用戶自定義名稱: %s\n", pstMVDevInfo->SpecialInfo.stCMLInfo.chUserDefinedName);printf("序列號: %s\n", pstMVDevInfo->SpecialInfo.stCMLInfo.chSerialNumber);printf("型號名稱: %s\n\n", pstMVDevInfo->SpecialInfo.stCMLInfo.chModelName);}else if (pstMVDevInfo->nTLayerType == MV_GENTL_CXP_DEVICE){// CoaXPress 設備printf("用戶自定義名稱: %s\n", pstMVDevInfo->SpecialInfo.stCXPInfo.chUserDefinedName);printf("序列號: %s\n", pstMVDevInfo->SpecialInfo.stCXPInfo.chSerialNumber);printf("型號名稱: %s\n\n", pstMVDevInfo->SpecialInfo.stCXPInfo.chModelName);}else if (pstMVDevInfo->nTLayerType == MV_GENTL_XOF_DEVICE){// XoF 設備(如光纖)printf("用戶自定義名稱: %s\n", pstMVDevInfo->SpecialInfo.stXoFInfo.chUserDefinedName);printf("序列號: %s\n", pstMVDevInfo->SpecialInfo.stXoFInfo.chSerialNumber);printf("型號名稱: %s\n\n", pstMVDevInfo->SpecialInfo.stXoFInfo.chModelName);}else{printf("不支持的設備類型!\n");}return true;
}/*** 工作線程函數:用于持續獲取圖像幀* 在觸發模式下,每發送一次軟件觸發,就嘗試獲取一幀圖像* @param pUser: 傳入的相機句柄(void* 類型)* @return 線程返回值(此處為 NULL)*/
static void* WorkThread(void* pUser)
{int nRet = MV_OK;MV_FRAME_OUT stImageInfo = {0}; // 存儲圖像數據和信息的結構體memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT));while(1){// 1. 發送軟件觸發命令,通知相機采集一幀圖像nRet = MV_CC_SetCommandValue(pUser, "TriggerSoftware");if (MV_OK != nRet){printf("發送軟件觸發命令失敗!錯誤碼: [0x%x]\n", nRet);}// 2. 獲取圖像緩沖區(等待最多1000ms)nRet = MV_CC_GetImageBuffer(pUser, &stImageInfo, 1000);if (nRet == MV_OK){// 成功獲取圖像printf("成功獲取一幀圖像:寬度[%d],高度[%d],幀號[%d]\n",stImageInfo.stFrameInfo.nWidth,stImageInfo.stFrameInfo.nHeight,stImageInfo.stFrameInfo.nFrameNum);// 使用完圖像緩沖區后必須釋放,否則內存泄漏MV_CC_FreeImageBuffer(pUser, &stImageInfo);}else{printf("獲取圖像失敗!錯誤碼: [0x%x]\n", nRet);}// 檢查是否收到退出信號if (g_bExit){break;}}return 0;
}/*** 主函數:實現海康相機的初始化、配置、觸發采集、停止和釋放資源*/
int main()
{int nRet = MV_OK;void* handle = NULL; // 相機設備句柄do {// 1. 初始化 SDK(必須首先調用)nRet = MV_CC_Initialize();if (MV_OK != nRet){printf("初始化SDK失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("SDK初始化成功。\n");// 2. 定義設備列表結構體并清零MV_CC_DEVICE_INFO_LIST stDeviceList;memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));// 3. 枚舉當前連接的所有支持的相機設備nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE, &stDeviceList);if (MV_OK != nRet){printf("枚舉設備失敗!錯誤碼: [0x%x]\n", nRet);break;}// 4. 檢查是否找到設備if (stDeviceList.nDeviceNum > 0){printf("共發現 %d 臺設備:\n", stDeviceList.nDeviceNum);for (int i = 0; i < stDeviceList.nDeviceNum; i++){printf("[設備 %d]:\n", i);MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];if (NULL == pDeviceInfo){printf("設備信息為空,跳過。\n");continue;}PrintDeviceInfo(pDeviceInfo); // 打印每臺設備的詳細信息}}else{printf("未發現任何相機設備!\n");break;}// 5. 提示用戶選擇要操作的相機printf("請輸入要打開的相機序號: ");unsigned int nIndex = 0;scanf("%d", &nIndex);if (nIndex >= stDeviceList.nDeviceNum){printf("輸入的序號無效!\n");break;}// 6. 根據用戶選擇創建設備句柄nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);if (MV_OK != nRet){printf("創建設備句柄失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("設備句柄創建成功。\n");// 7. 打開設備(建立通信)nRet = MV_CC_OpenDevice(handle);if (MV_OK != nRet){printf("打開設備失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("設備打開成功。\n");// 8. (僅對GigE相機)設置最優網絡包大小,提升傳輸效率if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE){int nPacketSize = MV_CC_GetOptimalPacketSize(handle);if (nPacketSize > 0){nRet = MV_CC_SetIntValueEx(handle, "GevSCPSPacketSize", nPacketSize);if (nRet != MV_OK){printf("警告:設置網絡包大小失敗!錯誤碼: [0x%x]\n", nRet);}else{printf("已設置最優網絡包大小為: %d 字節。\n", nPacketSize);}}else{printf("警告:獲取最優網絡包大小失敗!返回值: [0x%x]\n", nPacketSize);}}// 9. 關閉采集幀率控制(使用默認幀率)nRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", false);if (MV_OK != nRet){printf("設置采集幀率使能失敗!錯誤碼: [0x%x]\n", nRet);break;}// 10. 設置為觸發模式:開啟nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 1); // 1 表示開啟if (MV_OK != nRet){printf("設置觸發模式失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("已設置為觸發模式(開啟)。\n");// 11. 設置觸發源為“軟件觸發”nRet = MV_CC_SetEnumValue(handle, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE);if (MV_OK != nRet){printf("設置觸發源為軟件觸發失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("已設置觸發源為:軟件觸發。\n");// 12. 開始取流(啟動圖像采集)nRet = MV_CC_StartGrabbing(handle);if (MV_OK != nRet){printf("啟動取流失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("開始取流...\n");// 13. 創建工作線程,用于在后臺獲取圖像pthread_t nThreadID;nRet = pthread_create(&nThreadID, NULL, WorkThread, handle);if (nRet != 0){printf("創建工作線程失敗!返回值: %d\n", nRet);break;}printf("工作線程已啟動。\n");// 14. 等待用戶按回車鍵退出PressEnterToExit();// 15. 停止取流nRet = MV_CC_StopGrabbing(handle);if (MV_OK != nRet){printf("停止取流失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("已停止取流。\n");// 16. 關閉設備nRet = MV_CC_CloseDevice(handle);if (MV_OK != nRet){printf("關閉設備失敗!錯誤碼: [0x%x]\n", nRet);break;}printf("設備已關閉。\n");// 17. 銷毀設備句柄nRet = MV_CC_DestroyHandle(handle);if (MV_OK != nRet){printf("銷毀設備句柄失敗!錯誤碼: [0x%x]\n", nRet);break;}handle = NULL;printf("設備句柄已銷毀。\n");} while (0); // 使用 do-while(0) 實現錯誤時 break 跳出// 異常退出時確保資源釋放if (handle != NULL){MV_CC_DestroyHandle(handle);handle = NULL;printf("異常退出,已銷毀設備句柄。\n");}// 18. 反初始化 SDK(釋放全局資源)MV_CC_Finalize();printf("SDK已反初始化,程序退出。\n");return 0;
}
Demo: Trigger_Image.cppg++ -g -o Trigger_Image Trigger_Image.cpp -I../../../../../include -Wl,-rpath=$(MVCAM_COMMON_RUNENV)/64 -L$(MVCAM_COMMON_RUNENV)/64 -lMvCameraControl -lpthreadclean:rm Trigger_Image -rf
? SDK初始化成功。
發現 1 臺設備:
[設備 0]:
設備型號: MV-CA060-10GC
IP地址: 192.168.1.100
自定義名: Camera_01請輸入設備編號: 0
? 設備打開成功。
? 網絡包大小設置為: 1500
? 已設置為軟件觸發模式。
? 開始取流,發送軟件觸發采集圖像...? 獲取圖像成功:寬[2448] 高[2048] 幀號[1]
? 獲取圖像成功:寬[2448] 高[2048] 幀號[2]
? 獲取圖像成功:寬[2448] 高[2048] 幀號[3]請按回車鍵退出程序。? 程序退出。
結語
本文通過一個完整示例,展示了如何使用海康MVS SDK 實現 軟件觸發圖像采集,代碼結構清晰,注釋詳盡,適合初學者快速上手。
掌握這套流程后,你可以進一步實現 硬件觸發、多相機同步、圖像處理流水線 等高級功能。