背景
什么是相机的预置点?预置点就是一条记录,它记录下云台的俯仰角、偏转角,镜头的焦距、相机的光圈、曝光、白平衡等拍照参数,储存在相机内部的SD卡上,并被赋予一个编号以供用户索引。用户可以让相机根据指定编号的预置点,将自身拍照参数恢复到预置点记录的参数值,以实现拍摄预定区域的画面,这在安防领域很常用。
海康相机的云台最多支持255个预置点,对于安防一般够了,但对巡检机器人
来说就不够,因为机器人是移动的,它的巡视面积非常大,很容易就突破这个数目。
预置点的用法
一般通过调用NET_DVR_PTZPreset
(需要先启动预览)或NET_DVR_PTZPreset_Other
来实现,网上很多示例,不表。
解决思路
因为预置点要占用存储卡空间,所以通过增大存储卡容量来扩展预置点个数是不现实的。
考虑到巡检机器人一般仅负责拍照,不负责识别,对照片的智能识别一般交给性能强大的服务器,所以照片的清晰度就可以适当降低,并保证识别成功率依然较高。
于是思路就有了,只记录相机的PTZ
参数(pan
偏转、tilt
俯仰、zoom
变焦),以确保目标在画面内,其他参数都通过打开相机的自动聚焦
、自动白平衡
等配置开关来实现,然后将获取到的PTZ参数存储到机器人的硬盘上,巡检的时候再逐个取出并调用。
因为硬盘的容量比相机SD卡大多了,所以预置点的个数会扩展到成百上千万个!
代码实现
带云台的相机
带云台的相机可以通过SDK函数NET_DVR_GetSTDConfig
和NET_DVR_SetSTDConfig
来实现,该函数在SDK的pdf文档里语焉不详,其实SDK所有函数的文档都存在pdf版比chm版旧的现象,最好去同目录下的chm文档里查阅用法。
获取云台的ptz信息
需要给NET_DVR_GetSTDConfig
传递NET_DVR_GET_PTZABSOLUTEEX
命令字
具体用法看代码(对外封装成ROS
服务)
bool get_ptz(std_srvs::Trigger::Request &req,
std_srvs::Trigger::Response &res)
{
DWORD channel = 1;
char status_buf[ISAPI_STATUS_LEN];
memset(status_buf, 0, ISAPI_STATUS_LEN);
NET_DVR_PTZABSOLUTEEX_CFG ptz_cfg;
memset(&ptz_cfg, 0, sizeof(ptz_cfg));
ptz_cfg.dwSize = sizeof(ptz_cfg);
NET_DVR_STD_CONFIG std_cfg;
memset(&std_cfg, 0, sizeof(std_cfg));
std_cfg.lpOutBuffer = &ptz_cfg;
std_cfg.dwOutSize = sizeof(ptz_cfg);
std_cfg.lpCondBuffer = &channel;
std_cfg.dwCondSize = sizeof(channel);
std_cfg.lpStatusBuffer = status_buf;
std_cfg.dwStatusSize = ISAPI_STATUS_LEN;
BOOL success = NET_DVR_GetSTDConfig(lUserID, NET_DVR_GET_PTZABSOLUTEEX, &std_cfg);
if (!success)
{
DWORD err_code = NET_DVR_GetLastError();
ROS_WARN("NET_DVR_GetSTDConfig NET_DVR_GET_PTZABSOLUTEEX fail! error code = %u\n", err_code);
res.success = false;
char str[32];
sprintf(str, "%u", err_code);
res.message = str;
return false;
}else{
ROS_INFO("NET_DVR_GetSTDConfig NET_DVR_GET_PTZABSOLUTEEX success!");
res.success = true;
NET_PTZ_INFO *p = &ptz_cfg.struPTZCtrl;
std::ostringstream os;
os << "pan:" << p->fPan << ", tilt:" << p->fTilt << ", zoom:" << p->fZoom << ", focus:" << p->dwFocus;
res.message = os.str();
return true;
}
}
设置ptz信息到云台
需要给NET_DVR_SetSTDConfig
传递NET_DVR_SET_PTZABSOLUTEEX
命令字,具体代码懒得写了,相信大家写得出😉
不带云台的相机
这种相机没法使用上面的接口,需要用到一个更老的接口NET_DVR_GetDVRConfig
以及命令字NET_DVR_GET_PTZPOS
,莫慌,都能在chm文档里找到。
获取相机的焦距
bool get_ptz_old(std_srvs::Trigger::Request &req,
std_srvs::Trigger::Response &res)
{
NET_DVR_PTZPOS ptz_cfg;
memset(&ptz_cfg, 0, sizeof(ptz_cfg));
DWORD bytes_returned;
BOOL success = NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_PTZPOS, 1, &ptz_cfg, sizeof(ptz_cfg), &bytes_returned);
if (!success)
{
DWORD err_code = NET_DVR_GetLastError();
ROS_WARN("NET_DVR_GetDVRConfig NET_DVR_GET_PTZPOS fail! error code = %u\n", err_code);
res.success = false;
char str[32];
sprintf(str, "%u", err_code);
res.message = str;
return false;
}else{
ROS_INFO("NET_DVR_GetDVRConfig NET_DVR_GET_PTZPOS success!");
res.success = true;
NET_DVR_PTZPOS *p = &ptz_cfg;
std::ostringstream os;
os << "pan:" << p->wPanPos << ", tilt:" << p->wTiltPos << ", zoom:" << p->wZoomPos;
res.message = os.str();
return true;
}
}
设置相机的焦距
bool set_ptz_old(qp_bot_msgs::Preset::Request &req,
qp_bot_msgs::Preset::Response &res)
{
NET_DVR_PTZPOS ptz_cfg;
memset(&ptz_cfg, 0, sizeof(ptz_cfg));
ptz_cfg.wAction = 4; // 定位Z参数
ptz_cfg.wZoomPos = req.preset_index; // 标定时记录的焦距
BOOL success = NET_DVR_SetDVRConfig(lUserID, NET_DVR_SET_PTZPOS, 1, &ptz_cfg, sizeof(ptz_cfg));
if (!success)
{
DWORD err_code = NET_DVR_GetLastError();
ROS_WARN("NET_DVR_SetDVRConfig NET_DVR_SET_PTZPOS fail! error code = %u\n", err_code);
res.result = false;
char str[32];
sprintf(str, "%u", err_code);
res.err_msg = str;
return false;
}else{
ROS_INFO("NET_DVR_SetDVRConfig NET_DVR_SET_PTZPOS success!");
res.result = true;
res.err_msg = "OK";
return true;
}
}
两种相机都存在,咋办?
如果机器人既可能搭载带云台的相机,也可能搭载不带云台的,则需要判断是哪一种,判断方法就是查询其能力
,看是否支持PTZ绝对值
(不能是相对值,这样就受相机当前状态的影响了)的获取和设置,如果支持,则说明是带云台的相机,用NET_DVR_GetSTDConfig
,否则就是不带云台的相机,用NET_DVR_GetDVRConfig
。
具体查询接口是NET_DVR_GetSTDAbility
函数,传递NET_DVR_GET_PTZABSOLUTEEX_CAPABILITIES
命令字.。
bool support_ptz(){
DWORD channel = 1;
char status_buf[ISAPI_STATUS_LEN];
memset(status_buf, 0, ISAPI_STATUS_LEN);
PTZAbsoluteEx ptz_abl;
memset(&ptz_abl, 0, sizeof(ptz_abl));
ptz_abl.dwSize = sizeof(ptz_abl);
NET_DVR_STD_ABILITY std_abl;
memset(&std_abl, 0, sizeof(std_abl));
std_abl.lpOutBuffer = status_buf;
std_abl.dwOutSize = ISAPI_STATUS_LEN;
std_abl.lpCondBuffer = &channel;
std_abl.dwCondSize = sizeof(channel);
std_abl.lpStatusBuffer = status_buf;
std_abl.dwStatusSize = ISAPI_STATUS_LEN;
BOOL success = NET_DVR_GetSTDAbility(lUserID, NET_DVR_GET_PTZABSOLUTEEX_CAPABILITIES, &std_abl);
if (!success)
{
DWORD err_code = NET_DVR_GetLastError();
ROS_WARN("NET_DVR_GetSTDAbility NET_DVR_GET_PTZABSOLUTEEX_CAPABILITIES fail! error code = %u\n", err_code);
return false;
}else{
ROS_INFO("NET_DVR_GetSTDAbility NET_DVR_GET_PTZABSOLUTEEX_CAPABILITIES success!");
return true;
}
}
总结
现阶段巡检机器人
还未形成产业规模,所以没有适合的相机。目前要么是借用安防领域的民用相机,要么借用工厂自动化领域的工业相机,非常苦逼。或许等原始积累起来后,有公司会投入资源研发适用本行业的相机。
静观其变吧。