软件触发(Software Trigger)
- 由 代码指令 让相机拍一张
- 调用:
ExecuteSoftwareTrigger() - 不需要接线、不需要外部信号
优点:
- 控制最灵活,想什么时候拍就什么时候拍
- 多相机可以代码级同步
- 适合:高精度定时采集(50ms、20ms、10ms)
缺点:
- PC 忙的时候,触发精度会掉一点
- 28 个相机一起触发,时序会有几毫秒偏差
硬件触发(Hardware Trigger)
最稳定、最同步、工业标准方案
- 用 IO 线 给相机一个 3.3V/5V 脉冲
- 一通电,所有相机同时拍照
- 同步精度:微秒级
优点:
- 28 台相机可以做到 完全同时曝光
- 不受 CPU 占用、系统延迟影响
- 运动抓拍、高精度同步必用
缺点:
- 要接线、要 IO 卡、要继电器 / PLC
- 成本高、布线麻烦
自由运行 / 连续采集(Free Run / Auto)
- 相机自己按帧率一直拍:25fps、30fps
- 不用触发,不用指令
优点:
- 最简单
- 不用管
缺点:
- 多相机完全不同步
信号触发(IO 信号、编码器、光耦)
属于硬件触发的分支:
- 编码器触发(走多少距离拍一张)
- 光电开关触发(有物体过来拍)
- PLC 触发
高频单帧取图的陷阱
ExecuteSoftwareTrigger与多次调用PhotoImage
区别在于 ExecuteSoftwareTrigger流启动后,无需触发,可以根据指令触发拍照
但是PhotoImage每次都会走全流程:创建流->推缓冲区->启动采集->取帧->释放流
仅适用于偶尔手动拍一张(如调试、预览)
代码刨析
首先是触发方式是根据官方给的API:arv_camera_set_string决定的

在动态库中添加两个函数
开启软件触发前台调用m_nErrCode = m_cam->SetSoftwareTrigger(true, m_szErrMsg);
m_cam->SetTriggerSelector("FrameStart");(我没有写因为默认开启就是帧触发)
m_cam->OpenStream();启动流
cpp
COMMON_ERR CDevice::SetSoftwareTrigger(bool enable, char* szMsg/*= NULL*/)
{
//校验相机是否有效(m_pGige不为空)
if (Invalidate()) return ERR_CAMERA_INVALID;
GError* error = NULL;
ArvCamera* camera = (ArvCamera*)m_pGige;
COMMON_ERR nErrCode = COMMON_OK;
// 设置 TriggerMode
arv_camera_set_string(camera, "TriggerMode", enable ? "On" : "Off", &error);
if (error) {
nErrCode = error->code;
if (szMsg) memcpy(szMsg, error->message, sizeof(tagErrMsg) - 1);
g_clear_error(&error);
return nErrCode;
}
// 开启时设置 TriggerSource = Software
if (enable) {
arv_camera_set_string(camera, "TriggerSource", "Software", &error);
if (error) {
nErrCode = error->code;
if (szMsg) memcpy(szMsg, error->message, sizeof(tagErrMsg) - 1);
g_clear_error(&error);
// 可选回滚
arv_camera_set_string(camera, "TriggerMode", "Off", nullptr);
return nErrCode;
}
}
return COMMON_OK;
}
在程序中调用m_nErrCode = m_cam->ExecuteSoftwareTrigger(m_szErrMsg);
cpp
// 执行软件触发
COMMON_ERR CDevice::ExecuteSoftwareTrigger(char* szMsg/*= NULL*/)
{
// 1. 校验相机是否有效(m_pGige不为空)
if (Invalidate()) return ERR_CAMERA_INVALID;
GError* error = NULL;
ArvCamera* camera = (ArvCamera*)m_pGige;
// 2. 核心调用:向相机发送软件触发指令
arv_camera_software_trigger(camera, &error);
COMMON_ERR nErrCode = COMMON_OK;
if (error) {
nErrCode = error->code;
if (szMsg) memcpy(szMsg, error->message, sizeof(tagErrMsg) - 1);
g_clear_error(&error);
}
return nErrCode;
}
之后相机会自己拍照,我们只需要读他拍照后反馈的流就可以
相比
COMMON_ERR CGige::PhotoImage(char* szMsg/*= NULL*/)中
cpp
COMMON_ERR CGige::PhotoImage(char* szMsg/*= NULL*/)
{
if (Invalidate()) return ERR_CAMERA_INVALID;
COMMON_ERR nErrCode = COMMON_OK;
GError* error = NULL;
ArvCamera* camera =(ArvCamera*)m_pGige;
int bits = GetBitCount();
m_nBitCount = (bits >= 24) ? bits : 8;
int width = arv_camera_get_integer(camera, "Width", NULL);
int height = arv_camera_get_integer(camera, "Height", NULL);
arv_camera_gv_select_stream_channel(camera, 0, NULL);
arv_camera_set_acquisition_mode(camera, ARV_ACQUISITION_MODE_SINGLE_FRAME, NULL);
guint payload = arv_camera_get_payload(camera, NULL);
ArvStream* stream = arv_camera_create_stream(camera, NULL, NULL, &error);
if (stream && payload > 0)
{
for (int i = 0; i < 1; i++)
arv_stream_push_buffer(stream, arv_buffer_new(payload, NULL));
InitRateSpeed();
arv_camera_start_acquisition(camera, &error);
if (!error)
{
ArvBuffer* buffer = arv_stream_pop_buffer(stream);
if (buffer && arv_buffer_get_status(buffer) == ARV_BUFFER_STATUS_SUCCESS)
{
size_t buffer_size = 0;
const uint8_t* data = reinterpret_cast<const uint8_t*>(arv_buffer_get_data(buffer, &buffer_size));
int nWidth = arv_buffer_get_image_width(buffer);
int nHeight = arv_buffer_get_image_height(buffer);
SaveBuffer(data, buffer_size, nWidth, nHeight);
}
if (buffer) g_clear_object(&buffer);
}
arv_camera_stop_acquisition(camera, NULL);
}
if (error)
{
nErrCode = error->code; //ERR_CREATE_STREAM;
if (szMsg) memcpy(szMsg, error->message, sizeof(tagErrMsg) - 1);
g_clear_error(&error);
}
if (stream) g_clear_object(&stream);
return nErrCode;
}
每次都会创建新的流
要节省大多时间