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

使用海康威视 SDK 实现软触发拍照(C语言完整示例 + 中文注释)

🌟 前言

在机器视觉项目中,我们经常需要通过 软件触发(Software Trigger) 来控制工业相机在特定时刻拍摄图像。相比连续采集(Continuous Mode),触发模式(Trigger Mode) 更加精准、节能,适用于流水线检测、运动同步等场景。

本文基于 海康威视(Hikvision)官方 MVS SDK,使用 C 语言实现一个完整的 软触发拍照程序,并提供:

✅ 完整可运行代码

✅ 中文详细注释

✅ 所有打印信息中文化

✅ 常见问题解析(如只拍一张就停)

⚠️ 本文代码在 Linux 下测试通过,Windows 用户需调整线程和头文件路径。

📦 程序功能概述

本程序实现以下功能:

枚举所有连接的海康相机
用户选择目标相机
设置为 软触发模式(Software Trigger)
启动取流
创建工作线程,每收到一帧图像就发送一次软触发
图像回调函数中打印图像信息
按回车键退出程序🔁 实现“拍一张 → 处理 → 再拍一张”的典型控制逻辑。

💻 完整代码(含中文注释)

    
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include "MvCameraControl.h"// 全局标志:是否已获取图像帧(用于控制触发节奏)
bool g_bIsGetImage = true;// 全局标志:是否退出程序
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); // 给线程留出退出时间
}/*** 打印设备信息(IP、型号、用户自定义名称)* 支持 GigE 和 USB 相机*/
bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo)
{if (NULL == pstMVDevInfo){printf("设备信息指针为空!\n");return false;}if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE){// 解析当前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){printf("设备型号名称: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName);printf("用户自定义名称: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);}else{printf("不支持的设备类型。\n");}return true;
}/*** 图像数据回调函数* 当相机捕获到一帧图像时,SDK 会自动调用此函数*/
void __stdcall ImageCallBackEx(unsigned char* pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser)
{if (pFrameInfo){printf("成功获取一帧图像,宽度[%d],高度[%d],帧编号[%d]\n",pFrameInfo->nExtendWidth, pFrameInfo->nExtendHeight, pFrameInfo->nFrameNum);// 模拟图像处理耗时(可选)// sleep(1);g_bIsGetImage = true; // 标记:已收到新图像,可以再次触发}
}/*** 工作线程:循环发送软触发命令* 只有在上一帧图像被处理后才发送下一次触发*/
static void* WorkThread(void* pUser)
{while (1){if (g_bExit){break;}if (true == g_bIsGetImage){// 发送软触发命令,告诉相机“现在拍一张照片”int nRet = MV_CC_SetCommandValue(pUser, "TriggerSoftware");/*int nRet = MV_CC_SetCommandValue(pUser, "TriggerSoftware");这行代码的意思是:“SDK,请告诉相机:现在拍一张照片!”🟢 执行完这行代码后:你的程序不会等待相机拍照而是立即返回,继续循环相机开始拍照(需要几毫秒到几十毫秒)*/if (MV_OK != nRet){printf("软触发失败,错误码[0x%x]\n", nRet);}else{printf("软触发成功,已发送拍照指令");// 去掉此处 sleep,避免阻塞触发逻辑printf(" [软触发成功,当前g_bIsGetImage=%d]\n", g_bIsGetImage);g_bIsGetImage = false; // 重置标志,等待下一帧图像到达}}else{printf("[等待图像返回中,g_bIsGetImage=%d]\n", g_bIsGetImage);sleep(1); // 避免空转占用CPUcontinue;}}return NULL;
}int main()
{int nRet = MV_OK;void* handle = NULL;  // 相机句柄do{// 初始化 SDKnRet = MV_CC_Initialize();if (MV_OK != nRet){printf("初始化SDK失败!错误码 [0x%x]\n", nRet);break;}// 设备信息列表MV_CC_DEVICE_INFO_LIST stDeviceList;memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));// 枚举连接的相机设备nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);if (MV_OK != nRet){printf("枚举设备失败!错误码 [0x%x]\n", nRet);break;}if (stDeviceList.nDeviceNum > 0){for (int i = 0; i < stDeviceList.nDeviceNum; i++){printf("[设备 %d]:\n", i);MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];if (NULL == pDeviceInfo){break;}PrintDeviceInfo(pDeviceInfo);}}else{printf("未找到任何相机设备!\n");break;}// 用户选择相机索引printf("请输入要打开的相机编号: ");unsigned int nIndex = 0;scanf("%d", &nIndex);if (nIndex >= stDeviceList.nDeviceNum){printf("输入的编号无效!\n");break;}// 创建设备句柄nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]);if (MV_OK != nRet){printf("创建设备句柄失败!错误码 [0x%x]\n", nRet);break;}// 打开设备nRet = MV_CC_OpenDevice(handle);if (MV_OK != nRet){printf("打开设备失败!错误码 [0x%x]\n", nRet);break;}// 如果是 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("警告:获取最优数据包大小失败,错误码 [0x%x]!\n", nPacketSize);}}// 关闭帧率控制(使用触发模式时通常关闭)nRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", false);if (MV_OK != nRet){printf("设置帧率使能失败!错误码 [0x%x]\n", nRet);break;}// 设置触发模式为“开启”nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 1); // 1 = Onif (MV_OK != nRet){printf("设置触发模式失败!错误码 [0x%x]\n", nRet);break;}// 设置触发源为“软件触发”nRet = MV_CC_SetEnumValue(handle, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE);if (MV_OK != nRet){printf("设置触发源失败!错误码 [0x%x]\n", nRet);break;}// 打印当前采集模式(Continuous=3, MultiFrame=2, SingleFrame=1)MVCC_ENUMVALUE stEnum = {0};nRet = MV_CC_GetEnumValue(handle, "AcquisitionMode", &stEnum);if (MV_OK == nRet){printf("当前采集模式 AcquisitionMode = %d", stEnum.nCurValue);if (stEnum.nCurValue == 1) printf(" (单帧模式)");else if (stEnum.nCurValue == 2) printf(" (多帧模式)");else if (stEnum.nCurValue == 3) printf(" (连续模式)");printf("\n");}else{printf("获取采集模式失败,错误码 = 0x%x\n", nRet);printf("可能该相机不支持此参数,或已被弃用\n");}// 注册图像数据回调函数nRet = MV_CC_RegisterImageCallBackEx(handle, ImageCallBackEx, handle);if (MV_OK != nRet){printf("注册图像回调函数失败!错误码 [0x%x]\n", nRet);break;}// 开始取流(启动图像采集)nRet = MV_CC_StartGrabbing(handle);if (MV_OK != nRet){printf("开始取流失败!错误码 [0x%x]\n", nRet);break;}// 创建工作线程用于发送软触发pthread_t nThreadID;nRet = pthread_create(&nThreadID, NULL, WorkThread, handle);if (nRet != 0){printf("创建线程失败,返回值 = %d\n", nRet);break;}// 等待用户按回车退出PressEnterToExit();// 停止取流nRet = MV_CC_StopGrabbing(handle);if (MV_OK != nRet){printf("停止取流失败!错误码 [0x%x]\n", nRet);break;}// 关闭设备nRet = MV_CC_CloseDevice(handle);if (MV_OK != nRet){printf("关闭设备失败!错误码 [0x%x]\n", nRet);break;}// 销毁句柄nRet = MV_CC_DestroyHandle(handle);if (MV_OK != nRet){printf("销毁句柄失败!错误码 [0x%x]\n", nRet);break;}handle = NULL;} while (0);// 异常退出时确保句柄被销毁if (handle != NULL){MV_CC_DestroyHandle(handle);handle = NULL;}// 反初始化SDKMV_CC_Finalize();printf("程序退出。\n");return 0;
}

📊 运行效果

[设备 0]:
设备型号: MV-CA060-10GC
当前IP: 192.168.1.100
用户名称: Camera_01请输入相机编号: 0
📊 当前采集模式 = 1 (单帧)
✅ 成功获取图像,宽[4024],高[3036],帧号[1]
🟢 软触发成功,已发送拍照指令 [g_bIsGetImage=1]
✅ 成功获取图像,宽[4024],高[3036],帧号[2]
...
请按回车键退出程序...

🎯 总结

本文提供了一个 稳定、可扩展的软触发相机控制框架,适用于:

工业检测
机器人抓取
触发同步
图像采集控制

你只需要在此基础上:

添加图像保存(BMP/PNG)
集成 OpenCV 处理
使用队列缓存图像
支持多相机

即可构建完整的视觉系统。


文章转载自:

http://w3wktDyi.nrzkg.cn
http://lT3TkR31.nrzkg.cn
http://fF8ESlJV.nrzkg.cn
http://1s0574fe.nrzkg.cn
http://a00kj1AA.nrzkg.cn
http://s1GY3jRk.nrzkg.cn
http://nWlEJPqA.nrzkg.cn
http://9FdEtCl1.nrzkg.cn
http://Hf0CP1Gc.nrzkg.cn
http://Y5WLeVPl.nrzkg.cn
http://GjS5JC3I.nrzkg.cn
http://Ebp0Rirw.nrzkg.cn
http://3n6NjPYW.nrzkg.cn
http://qzzx6Wwi.nrzkg.cn
http://wVsrl0Hz.nrzkg.cn
http://9FyeWamh.nrzkg.cn
http://ZZrG4jlz.nrzkg.cn
http://KD5pjTaW.nrzkg.cn
http://rKn6fOrZ.nrzkg.cn
http://Mo2fi6FG.nrzkg.cn
http://uM5XFPK6.nrzkg.cn
http://MqA0Lt9u.nrzkg.cn
http://oJldDlD6.nrzkg.cn
http://nmfDXdbA.nrzkg.cn
http://0E5WcNqk.nrzkg.cn
http://7DgPdmsL.nrzkg.cn
http://2xMqjNba.nrzkg.cn
http://LrkNey2N.nrzkg.cn
http://X3WwtSAt.nrzkg.cn
http://J9sm5b8D.nrzkg.cn
http://www.dtcms.com/a/369719.html

相关文章:

  • 本科论文抽检档案整理:Python批量文件查找、打包、改名
  • 【Day 22】94.二叉树的中序遍历 104.二叉树的最大深度 226.翻转二叉树 101.对称二叉树
  • swing笔记
  • IPD模式下跨部门团队管理
  • Transformer核心—自注意力机制
  • 可搜索且多选的下拉式列表
  • 《C++ printf()函数的深度解析》
  • HTML基础(决定页面结构)
  • Modbus RTU 协议介绍
  • 掌握RabbitMQ核心战法:从消息确认到高可用集群
  • C++数据结构命名:从规范到艺术的深度解析
  • 前后端国密加密传输用户密码流程
  • [2025.9.5]Win11.26H2.27934.1 IoT 金丝雀轻度精简优化版 PIIS出品
  • 无名信号量
  • IPD变革,是中国企业实现产品与技术领先之路
  • 在Windows中已经启动的容器(比如xinference),如何设置让其在每次Docker启动时能自动启动
  • 支付DDD建模
  • Nginx 配置详解与虚拟主机实战指南
  • 驱动员工的核心:少谈“大道理”,多解“人心”
  • 【LLM】使用 Transformer 强化学习的 GRPO
  • 【代码随想录算法训练营——Day3】链表——203.移除链表元素、707.设计链表、206.反转链表
  • 目标检测双雄:一阶段与二阶段检测器全解析
  • 2025高教社数学建模国赛C题 - NIPT的时点选择与胎儿的异常判定(完整参考论文)
  • keil 5 STM32工程介绍
  • C/C++包管理工具:Conan
  • 标注格式转换csv转xml
  • 错误是ModuleNotFoundError: No module named ‘pip‘解决“找不到 pip”
  • 文章采集发布帝国ECMS网站技巧
  • 创新、绿色、共赢:芬兰企业在华发展战略与案例解析(2025中芬建交75周年)
  • PAIN | 痛在你身,激活在我脑:原来后侧默认模式网络是‘感同身受’的神经开关