突破海康相机255预置点的限制

背景

什么是相机的预置点?预置点就是一条记录,它记录下云台的俯仰角、偏转角,镜头的焦距、相机的光圈、曝光、白平衡等拍照参数,储存在相机内部的SD卡上,并被赋予一个编号以供用户索引。用户可以让相机根据指定编号的预置点,将自身拍照参数恢复到预置点记录的参数值,以实现拍摄预定区域的画面,这在安防领域很常用。
海康相机的云台最多支持255个预置点,对于安防一般够了,但对巡检机器人来说就不够,因为机器人是移动的,它的巡视面积非常大,很容易就突破这个数目。

预置点的用法

一般通过调用NET_DVR_PTZPreset(需要先启动预览)或NET_DVR_PTZPreset_Other来实现,网上很多示例,不表。
突破海康相机255预置点的限制_第1张图片

解决思路

因为预置点要占用存储卡空间,所以通过增大存储卡容量来扩展预置点个数是不现实的。
考虑到巡检机器人一般仅负责拍照,不负责识别,对照片的智能识别一般交给性能强大的服务器,所以照片的清晰度就可以适当降低,并保证识别成功率依然较高。
于是思路就有了,只记录相机的PTZ参数(pan偏转、tilt俯仰、zoom变焦),以确保目标在画面内,其他参数都通过打开相机的自动聚焦自动白平衡等配置开关来实现,然后将获取到的PTZ参数存储到机器人的硬盘上,巡检的时候再逐个取出并调用。
因为硬盘的容量比相机SD卡大多了,所以预置点的个数会扩展到成百上千万个!

代码实现

带云台的相机

带云台的相机可以通过SDK函数NET_DVR_GetSTDConfigNET_DVR_SetSTDConfig来实现,该函数在SDK的pdf文档里语焉不详,其实SDK所有函数的文档都存在pdf版比chm版旧的现象,最好去同目录下的chm文档里查阅用法。
突破海康相机255预置点的限制_第2张图片

获取云台的ptz信息

需要给NET_DVR_GetSTDConfig传递NET_DVR_GET_PTZABSOLUTEEX命令字
突破海康相机255预置点的限制_第3张图片

具体用法看代码(对外封装成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;
    }
}

总结

现阶段巡检机器人还未形成产业规模,所以没有适合的相机。目前要么是借用安防领域的民用相机,要么借用工厂自动化领域的工业相机,非常苦逼。或许等原始积累起来后,有公司会投入资源研发适用本行业的相机。
静观其变吧。

你可能感兴趣的:(机器人,C/C++,巡检机器人,C++,ROS,海康,相机)