rm_vision/io/hikrobot/hikrobot.cpp
2025-12-15 02:33:20 +08:00

246 lines
6.3 KiB
C++

#include "hikrobot.hpp"
#include <libusb-1.0/libusb.h>
#include "tools/logger.hpp"
using namespace std::chrono_literals;
namespace io
{
HikRobot::HikRobot(double exposure_ms, double gain, const std::string & vid_pid)
: exposure_us_(exposure_ms * 1e3), gain_(gain), queue_(1), daemon_quit_(false), vid_(-1), pid_(-1)
{
set_vid_pid(vid_pid);
if (libusb_init(NULL)) tools::logger()->warn("Unable to init libusb!");
daemon_thread_ = std::thread{[this] {
tools::logger()->info("HikRobot's daemon thread started.");
capture_start();
while (!daemon_quit_) {
std::this_thread::sleep_for(100ms);
if (capturing_) continue;
capture_stop();
reset_usb();
capture_start();
}
capture_stop();
tools::logger()->info("HikRobot's daemon thread stopped.");
}};
}
HikRobot::~HikRobot()
{
daemon_quit_ = true;
if (daemon_thread_.joinable()) daemon_thread_.join();
tools::logger()->info("HikRobot destructed.");
}
void HikRobot::read(cv::Mat & img, std::chrono::steady_clock::time_point & timestamp)
{
CameraData data;
queue_.pop(data);
img = data.img;
timestamp = data.timestamp;
}
void HikRobot::capture_start()
{
capturing_ = false;
capture_quit_ = false;
unsigned int ret;
MV_CC_DEVICE_INFO_LIST device_list;
ret = MV_CC_EnumDevices(MV_USB_DEVICE, &device_list);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_EnumDevices failed: {:#x}", ret);
return;
}
if (device_list.nDeviceNum == 0) {
tools::logger()->warn("Not found camera!");
return;
}
ret = MV_CC_CreateHandle(&handle_, device_list.pDeviceInfo[0]);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_CreateHandle failed: {:#x}", ret);
return;
}
ret = MV_CC_OpenDevice(handle_);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_OpenDevice failed: {:#x}", ret);
return;
}
set_enum_value("BalanceWhiteAuto", MV_BALANCEWHITE_AUTO_CONTINUOUS);
set_enum_value("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF);
set_enum_value("GainAuto", MV_GAIN_MODE_OFF);
set_float_value("ExposureTime", exposure_us_);
set_float_value("Gain", gain_);
MV_CC_SetFrameRate(handle_, 150);
ret = MV_CC_StartGrabbing(handle_);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_StartGrabbing failed: {:#x}", ret);
return;
}
capture_thread_ = std::thread{[this] {
tools::logger()->info("HikRobot's capture thread started.");
capturing_ = true;
MV_FRAME_OUT raw;
MV_CC_PIXEL_CONVERT_PARAM cvt_param;
while (!capture_quit_) {
std::this_thread::sleep_for(1ms);
unsigned int ret;
unsigned int nMsec = 100;
ret = MV_CC_GetImageBuffer(handle_, &raw, nMsec);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_GetImageBuffer failed: {:#x}", ret);
break;
}
auto timestamp = std::chrono::steady_clock::now();
cv::Mat img(cv::Size(raw.stFrameInfo.nWidth, raw.stFrameInfo.nHeight), CV_8U, raw.pBufAddr);
cvt_param.nWidth = raw.stFrameInfo.nWidth;
cvt_param.nHeight = raw.stFrameInfo.nHeight;
cvt_param.pSrcData = raw.pBufAddr;
cvt_param.nSrcDataLen = raw.stFrameInfo.nFrameLen;
cvt_param.enSrcPixelType = raw.stFrameInfo.enPixelType;
cvt_param.pDstBuffer = img.data;
cvt_param.nDstBufferSize = img.total() * img.elemSize();
cvt_param.enDstPixelType = PixelType_Gvsp_BGR8_Packed;
// ret = MV_CC_ConvertPixelType(handle_, &cvt_param);
const auto & frame_info = raw.stFrameInfo;
auto pixel_type = frame_info.enPixelType;
cv::Mat dst_image;
const static std::unordered_map<MvGvspPixelType, cv::ColorConversionCodes> type_map = {
{PixelType_Gvsp_BayerGR8, cv::COLOR_BayerGR2RGB},
{PixelType_Gvsp_BayerRG8, cv::COLOR_BayerRG2RGB},
{PixelType_Gvsp_BayerGB8, cv::COLOR_BayerGB2RGB},
{PixelType_Gvsp_BayerBG8, cv::COLOR_BayerBG2RGB}};
cv::cvtColor(img, dst_image, type_map.at(pixel_type));
img = dst_image;
queue_.push({img, timestamp});
ret = MV_CC_FreeImageBuffer(handle_, &raw);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_FreeImageBuffer failed: {:#x}", ret);
break;
}
}
capturing_ = false;
tools::logger()->info("HikRobot's capture thread stopped.");
}};
}
void HikRobot::capture_stop()
{
capture_quit_ = true;
if (capture_thread_.joinable()) capture_thread_.join();
unsigned int ret;
ret = MV_CC_StopGrabbing(handle_);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_StopGrabbing failed: {:#x}", ret);
return;
}
ret = MV_CC_CloseDevice(handle_);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_CloseDevice failed: {:#x}", ret);
return;
}
ret = MV_CC_DestroyHandle(handle_);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_DestroyHandle failed: {:#x}", ret);
return;
}
}
void HikRobot::set_float_value(const std::string & name, double value)
{
unsigned int ret;
ret = MV_CC_SetFloatValue(handle_, name.c_str(), value);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_SetFloatValue(\"{}\", {}) failed: {:#x}", name, value, ret);
return;
}
}
void HikRobot::set_enum_value(const std::string & name, unsigned int value)
{
unsigned int ret;
ret = MV_CC_SetEnumValue(handle_, name.c_str(), value);
if (ret != MV_OK) {
tools::logger()->warn("MV_CC_SetEnumValue(\"{}\", {}) failed: {:#x}", name, value, ret);
return;
}
}
void HikRobot::set_vid_pid(const std::string & vid_pid)
{
auto index = vid_pid.find(':');
if (index == std::string::npos) {
tools::logger()->warn("Invalid vid_pid: \"{}\"", vid_pid);
return;
}
auto vid_str = vid_pid.substr(0, index);
auto pid_str = vid_pid.substr(index + 1);
try {
vid_ = std::stoi(vid_str, 0, 16);
pid_ = std::stoi(pid_str, 0, 16);
} catch (const std::exception &) {
tools::logger()->warn("Invalid vid_pid: \"{}\"", vid_pid);
}
}
void HikRobot::reset_usb() const
{
if (vid_ == -1 || pid_ == -1) return;
// https://github.com/ralight/usb-reset/blob/master/usb-reset.c
auto handle = libusb_open_device_with_vid_pid(NULL, vid_, pid_);
if (!handle) {
tools::logger()->warn("Unable to open usb!");
return;
}
if (libusb_reset_device(handle))
tools::logger()->warn("Unable to reset usb!");
else
tools::logger()->info("Reset usb successfully :)");
libusb_close(handle);
}
} // namespace io