海康威視工業相機SDK開發實戰:使用C/C++實現軟件觸發圖像采集(含詳細中文注釋代碼)

一、前言

在機器視覺、自動化檢測、智能制造等領域,工業相機是獲取圖像數據的核心設備。海康威視作為國內領先的機器視覺廠商,其工業相機產品線豐富,廣泛應用于各類工業場景。

本文將帶你從零開始,使用 海康MVS SDK(Machine Vision Software Development Kit),通過 C/C++語言 實現對海康工業相機的控制,重點演示如何配置 軟件觸發模式 并完成圖像采集。

? 本文特點:全中文輸出日志詳細中文注釋結構清晰,適合初學者基于真實SDK接口編寫

二、開發環境準備

  1. 硬件準備

    海康威視工業相機(GigE / USB3.0 均可)
    網線(GigE)或 USB3.0 線(USB)
    相機已正確連接并上電

  2. 軟件準備

    操作系統: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 實現 軟件觸發圖像采集,代碼結構清晰,注釋詳盡,適合初學者快速上手。

掌握這套流程后,你可以進一步實現 硬件觸發、多相機同步、圖像處理流水線 等高級功能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98033.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98033.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98033.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Modbus RTU 協議介紹

Modbus RTU 協議介紹 異步串行傳輸方式&#xff0c;采用二進制格式&#xff0c;適用于串行通訊&#xff08;如RS-485&#xff09;&#xff0c;效率高&#xff0c;是工業現場的主流選擇。 主站是Master&#xff0c;從站是Slave。 Modbus RTU 協議格式 幀結構 地址碼&#xf…

TCP/IP函數——sendmsg

sendmsg() 是 POSIX 標準中一個高級套接字發送函數,屬于系統調用(由操作系統內核實現),定義在 <sys/socket.h> 頭文件中。它的核心特點是支持復雜消息結構,不僅能發送常規數據,還能附加控制信息(如輔助數據、IP 選項等),適用于 TCP、UDP 等多種協議,功能比 sen…

運動控制中的插值運動(插補運動):原理、實現與應用

在自動化設備中,從起點到終點的精準軌跡控制是核心需求。當目標軌跡是直線、圓弧或復雜曲線時,僅通過離散的目標點無法實現平滑運動,這就需要插值運動(Interpolation Motion)技術 —— 通過控制算法在已知路徑點之間計算出連續的中間點,使運動部件沿預定軌跡平滑移動。本…

GMT——用于人形全身控制的通用運動跟蹤:兩階段師生訓練框架下,全身基于單一策略,且自適應采樣、MoE架構

前言 如此文《KungfuBot——基于物理約束和自適應運動追蹤的人形全身控制PBHC&#xff0c;用于學習打拳或跳舞(即RL下的動作模仿和運控)》的開頭所說 如此&#xff0c;便關注到最新出來的三個工作 第一個是GMT: General Motion Tracking for Humanoid Whole-Body Control第二個…

matlab版本粒子群算法(PSO)在路徑規劃中的應用

基于粒子群優化&#xff08;PSO&#xff09;算法的路徑規劃 MATLAB代碼實現 1. 初始化環境和參數 % 初始化環境參數 mapSize [10, 10]; % 地圖大小 startPoint [1, 1]; % 起點 endPoint [9, 9]; % 終點 obstacles [3, 3; 5, 5; 7, 7]; % 障礙物位置% PSO參數 numParticles …

Go語言面試:傳值與傳引用的區別及選擇指南

在Go語言中&#xff0c;函數參數的傳遞方式有兩種&#xff1a;傳值&#xff08;pass-by-value&#xff09;和傳引用&#xff08;pass-by-reference&#xff09;。理解這兩種方式的區別及其適用場景&#xff0c;是成為Go語言開發高手的必備技能。本文將深入探討Go語言中傳值與傳…

數據無言,網關有聲 耐達訊自動化RS485轉Profinet讓千年液位數據“開口說話”

在能源行業的數字化轉型浪潮中&#xff0c;你是否曾面臨這樣的困境&#xff1a; 現場大量采用RS485接口的液位計&#xff0c;數據孤立如信息孤島&#xff0c;無法接入Profinet高速網絡&#xff1f; 模擬信號傳輸距離受限&#xff0c;抗干擾能力弱&#xff0c;導致液位測量誤差…

出口退稅新政大提速:企業如何抓住政策紅利,提升最高13%純利?

近年來&#xff0c;出口退稅政策的優化與升級&#xff0c;正在成為外貿企業提升資金周轉率和利潤率的關鍵。國家稅務總局發布的 2022年第9號公告&#xff08;簡稱“9號公告”&#xff09;落地執行已兩年&#xff0c;外貿行業普遍感受到退稅速度顯著加快&#xff0c;平均退稅周期…

使用pytorch創建/訓練/推理OCR模型

一、任務描述 從手寫數字圖像中自動識別出對應的數字&#xff08;0-9&#xff09;” 的問題&#xff0c;屬于單標簽圖像分類任務&#xff08;每張圖像僅對應一個類別&#xff0c;即 0-9 中的一個數字&#xff09; 1、任務的核心定義&#xff1a;輸入與輸出 輸入&#xff1a;28…

新啟航開啟深孔測量新紀元:激光頻率梳技術攻克光學遮擋,達 130mm 深度 2μm 精度

摘要&#xff1a;本文聚焦于深孔測量領域&#xff0c;介紹了一種創新的激光頻率梳技術。該技術成功攻克傳統測量中的光學遮擋難題&#xff0c;在深孔測量深度達 130mm 時&#xff0c;可實現 2μm 的高精度測量&#xff0c;為深孔測量開啟了新的發展篇章。關鍵詞&#xff1a;激光…

GEO優化推薦:AI搜索新紀元下的品牌內容權威構建

引言&#xff1a;AI搜索引擎崛起與GEO策略的戰略重心轉移2025年&#xff0c;以ChatGPT、百度文心一言、DeepSeek為代表的AI搜索引擎已深入成為公眾信息獲取的核心渠道。這標志著品牌營銷策略的重心&#xff0c;正從傳統的搜索引擎優化&#xff08;SEO&#xff09;加速向生成式引…

uniapp的上拉加載H5和小程序

小程序配置{"path": "list/course-list","style": {"navigationBarTitleText": "課程列表","enablePullDownRefresh": true,"onReachBottomDistance": 150}}上拉拉觸底鉤子onReachBottom() {var that …

【和春筍一起學C++】(四十)抽象數據類型

抽象數據類型&#xff08;abstract data type, ADT&#xff09;以通用的方式描述數據類型。C中類的概念非常適合于ADT方法。例如&#xff0c;C程序通過堆棧來管理自動變量&#xff0c;堆棧可由對它執行的操作來描述。可創建空堆棧&#xff1b;可將數據項添加到堆頂&#xff08;…

大文件斷點續傳解決方案:基于Vue 2與Spring Boot的完整實現

大文件斷點續傳解決方案:基于Vue 2與Spring Boot的完整實現 在現代Web應用中,大文件上傳是一個常見但具有挑戰性的需求。傳統的文件上傳方式在面對網絡不穩定、大文件傳輸時往往表現不佳。本文將詳細介紹如何實現一個支持斷點續傳的大文件上傳功能,結合Vue 2前端和Spring Bo…

LeNet-5:手寫數字識別經典CNN

配套講解視頻&#xff0c;點擊下方名片獲取20 世紀 90 年代&#xff0c;計算機已經能識別文本&#xff0c;但圖片識別很困難。比如銀行支票的手寫數字識別&#xff0c;傳統方法需要人工設計規則&#xff0c;費時費力且精度不高。 于是&#xff0c;Yann LeCun 及其團隊提出了 Le…

如何在 C# 中將文本轉換為 Word 以及將 Word 轉換為文本

在現代軟件開發中&#xff0c;處理文檔內容是一個非常常見的需求。無論是生成報告、存儲日志&#xff0c;還是處理用戶輸入&#xff0c;開發者都可能需要在純文本與 Word 文檔之間進行轉換。有時需要將文本轉換為 Word&#xff0c;以便生成結構化的 .docx 文件&#xff0c;使內…

Open SWE:重構代碼協作的智能范式——從規劃到PR的全流程自動化革命

在軟件開發的演進史上,工具鏈的每一次革新都深刻重塑著開發者的工作方式。LangChain AI推出的Open SWE,作為首個開源的異步編程代理,正在重新定義代碼協作的邊界——它不再僅僅是代碼生成工具,而是構建了從代碼庫分析、方案規劃、代碼實現到拉取請求創建的端到端自動化工作…

【ARDUINO】通過ESP8266控制電機【待測試】

需求 通過Wi-Fi控制Arduino驅動的3V直流電機。這個方案使用外部6V或9V電源&#xff0c;ESP8266作為Wi-Fi模塊&#xff0c;Arduino作為主控制器&#xff0c;L298N作為電機驅動器。 手機/電腦 (Wi-Fi客戶端) | | (Wi-Fi) | ESP8266 (Wi-Fi模塊, AT指令模式) | | (串口通信) | A…

cuda編程筆記(18)-- 使用im2col + GEMM 實現卷積

我們之前介紹了cudnn調用api直接實現卷積&#xff0c;本文我們探究手動實現。對于直接使用for循環在cpu上的實現方法&#xff0c;就不過多介紹&#xff0c;只要了解卷積的原理&#xff0c;就很容易實現。im2col 的核心思想im2col image to column把輸入 feature map 的每個卷積…

Loopback for Mac:一鍵打造虛擬音頻矩陣,實現跨應用音頻自由流轉

虛擬音頻設備創建 模擬物理設備&#xff1a;Loopback允許用戶在Mac上創建虛擬音頻設備&#xff0c;這些設備可被系統及其他應用程序識別為真實硬件&#xff0c;實現音頻的虛擬化傳輸。多源聚合&#xff1a;支持將麥克風、應用程序&#xff08;如Skype、Zoom、GarageBand、Logic…