云台控制

最近学习了云台的一些东西,比如云台的协议阿,云台的调试什么,通过这段时间努力终于把几个问题解决了,一个是对于某个厂商(A)的云台出现的 pelco-p 协议在波特率 2400 4800 9600 协议、地址码设置和球上的一致的情况下不能控制云台的转动,另外一个问题是A的云台 ( 恒速球 ) 不支持预置点、到达某个预置点、巡航线的功能,

      对云台的开发来说最主要的是要能够结合说明书、理解协议并且实现对云台写串口数据。

                                基类 :PTZProtocal

                                                        |

                                  |--------|---------|---------|--------|

                     |      |       |       |     |

                PTZPelcod ……………………………….. PTZPelcod

      再加上一个对串口进行读写的类就可以组合完成了。

      现在的云台大多都是通过 485 接口进行控制的,在进行云台开发前最好了配备了 232 ß -------->485 这样的接口和串口的调试工具,通过对云台直接发送串口命令来验证云台是好的,这样才能够放心的用来开发,否则本来就有问题的云台会给你在开发过程中带来很多的困惑和莫名其妙的问题。

      云台最主要的几个协议 pelco-p pelco-d 协议,这也是我工作中碰到最多的两个协议,关于这两个协议的内容网上很多,我这也不再说明,这里我只谈谈在云台开发的过程中遇到的几个问题:

A的云台 :

问题 1 PELCOD 协议的实现和 PELCOP 协议的实现有差别,会导致在设置地址值的时候需要比球上设置的地址值大一才能够控制球 ( 说明书上也有说对于不同的协议,有的地址从 0 开始,有的地址从 1 开始 ) ,所以需要问厂商能不能有种方式可以获取云台的信息得知云台是A的

问题 2 :预制点和巡航线的问题 , 通过串口调试工具用串口给 PTZ 发送命令 : 设置预置点 1 2 ,并且发送转到预置点 1 的命令, PTZ 会不停的来回左右转,当发送转至预置点 2 的命令, PTZ 停止转动。通过咨询供应商来解决这个问题

对于问题 1 ,我们想的那种方法看来是不行的,厂商那边告诉我们无法通过云台启动的时候读取串口信息或者启动之后发送某个命令可以获取到是本场上的设备,唯一的做法只有通过用户自己选购并选择所使用的云台是那个厂家的了

对于问题 2,( 供应商那边的说法: JM8507 ,(J 型的 ) 侧方向挂解码板的A的云台不支持预置点、跳转到某个预置点、巡航线的功能,而 V 型的可以 ( 解码板在吊装口处 )) , 原来郁闷了半天的问题都是出在云台本身了。这也让我吸取了教训,先要对开发的东西有个了解,特别是对于那些厂家开发的标准不统一的那部分得开发更要熟读它 的说明书才不至于在开发过程中栽倒到设备本身的问题,不过有时条件不允许你花太多时间区熟悉了...这就要看个人查找问题的经验、效率与能力了.



前言:本文通过一个简单的工程描述了一种插件设计的思想。复杂设计和模块化分解能力是衡量一个程序员水平的重要标志,欢迎大家和我探讨。
1.我们的目的是在Plug-Ins目录里面查找“ptz*.dll”格式的云台插件的动态链接库。每个库可支持多个协议,通过查找该目录下的合法插件,有此插件创建出某个协议的实例,达到通过该协议操作云台的目的。
2.结构体定义: ComParam 为串口通信参数:(定义在include/ComManager/ComParam.h文件中)

class COMPARAMEXT_API ComParam

{

public :

       ComParam(  int nBaudRate,                         // 波特率

                              int nByteSize = 8 ,                  // 数据位

                              int nParity = 0 ,                       // 校验位 0 - NOPARITY

                              int nStopBits = 0 );                 // 停止位 0 - ONESTOPBIT( 参考 winbase.h 中的定义)

};

3.为了达到使用dll的目的,先封装几个简单的类:

CDllLoad类 :src/PTZ_Test/controls/DllLoad.h(这个类仅在src/PTZ_Test/controls/PTZDll.h)

// DllLoad.h
#pragma once

class CDllLoad
{
public :

    CDllLoad()

    {
    }
    virtual ~CDllLoad(void )
    {
         Close();
    }

    void Open(LPCTSTR lpszName) // 加载DLL
    {
         if (m_hModule)
         {
              Close();
         }
         m_hModule = ::LoadLibrary(lpszName);
    }

    void Close()
    {
         ::FreeLibrary(m_hModule);  // 卸载DLL
         m_hModule = NULL;
    }

    operator   HMODULE() { return m_hModule;}

    PROC GetProcAddress(LPCSTR lpProcName)
    {
         return ::GetProcAddress(m_hModule, lpProcName);    // 取得函数入口地址
    }

protected :
    HMODULE m_hModule;
};
CPTZDll类 :src/PTZ_Test /controls/DllLoad.h(主要功能:根据插件产生出该DLL的支持的协议对象,然后由该对象的函数调用需要的功能,由于函数调用时实际使 用的是dll的类的成员函数,所以对象存在时必须也有一份CPTZDll的实例而且是打开状态的。)
// PTZDll.h
#pragma once

#include "DllLoad.h"
#include "PTZInterface.h"

class CPTZDll
{
    typedef CPTZInterface * (__stdcall* CREATE_PTZ_INSTANCE)(const char * pszProtocolsName);
public :
    CPTZDll()    // 成员初始化
        : m_pDllLoad(0 )
        , m_pfnCreatePTZInstance(0 )
        , m_ppProtocolsName(0 )
    {
    }

    ~CPTZDll()
    {
         Close();

         delete m_pDllLoad;
         m_pDllLoad = 0 ;
    }

    BOOL OpenPlugIn(LPCTSTR lpszName)    // 打开插件,如果是有效的插件返回TRUE,否则FALSE
    {
         if (m_pDllLoad)
              Close();
         else
         {
              m_pDllLoad = new CDllLoad();
              m_pDllLoad- >Open(lpszName);
         }

         m_pfnCreatePTZInstance = (CREATE_PTZ_INSTANCE)m_pDllLoad- >GetProcAddress("CreatePTZInstance" );
         m_ppProtocolsName = (const char **)m_pDllLoad- >GetProcAddress("ProtocolNames" );

         return (m_ppProtocolsName && m_ppProtocolsName[0 ]);
    }

    void Close()
    {
         m_pfnCreatePTZInstance = 0 ;
         m_ppProtocolsName = 0 ;

        if (m_pDllLoad)
            m_pDllLoad- >Close();
    }

    const char ** GetSupportProtocols()
    {
        return m_ppProtocolsName;
    }

    CPTZInterface * CreatePTZInstance(LPCTSTR lpszProtolName)    // 根据该插件内的某个协议产生出一个对象
    {
            if (m_pfnCreatePTZInstance)
        {
            return m_pfnCreatePTZInstance(lpszProtolName);
        }

        return 0 ;
}

private :
    CDllLoad * m_pDllLoad;

    CREATE_PTZ_INSTANCE m_pfnCreatePTZInstance;

    const char ** m_ppProtocolsName;
};
CPlugInInfo类 :src/PTZ_Test/controls/PTZProtocolFind.h和src/PTZ_Test/controls/ptzprotocolfind.cpp

//=========================================================
//

// Copyright (c) 2000-2004  iWise Technologies,Co. Ltd.
// All Rights Reserved.
//
// Product: iW988
// File: PTZProtocolFind.h
// Created: 天衣有缝
// Data :2004.12.22 PM

// Description:
//     ValueAdded main program for iW988.
//                   Contact:
//                       waterpub@mail.csdn.net
//
//=========================================================

#pragma once

 

class CPlugInInfo    // 插件信息类
{
public :
    string szFileName                ; // 文件名
    vector<string>                      Protocols                   ; // 协议列表
    string szFullPath    ; // 文件全路径

public :
    CPlugInInfo(void )
    {
    }
    ~CPlugInInfo(void )
    {
    }
};

class CPTZProtocolFind
{
public :
    CPTZProtocolFind(void );
    ~CPTZProtocolFind(void );

public :
    void EnumPlugIns(vector<CPlugInInfo>& PlugInList); // 枚举Plug-Ins目录的所以ptz文件
};

//=========================================================
//

// Copyright (c) 2000-2004  iWise Technologies,Co. Ltd.
// All Rights Reserved.
//
// Product: iW988
// File: PTZProtocolFind.cpp
// Created: 天衣有缝
// Data :2004.12.22 PM
// Description:
//     ValueAdded main program for iW988.
//                   Contact:
//                       waterpub@mail.csdn.net
//
//=========================================================

#include
 "StdAfx.h"
 
#include "ptzprotocolfind.h"
#include "PTZDll.h"

CPTZProtocolFind::CPTZProtocolFind(void )
{
}

CPTZProtocolFind::~CPTZProtocolFind(void )
{
}
// 枚举“Plug-Ins”目录下的所有有效插件

void CPTZProtocolFind::EnumPlugIns(vector<CPlugInInfo>& PlugInList)
{
    char szFilePath[MAX_PATH];
    ::GetModuleFileName(NULL, szFilePath, MAX_PATH);
    char * pFind = strrchr(szFilePath, '//' );
    *pFind = '/0' ;
    strcat(szFilePath, "//Plug-Ins//ptz*.dll" ); // 查找的匹配字符串

    CFileFind find;
    BOOL bFind;
    bFind = find.FindFile(szFilePath);
    while (bFind)
    {
        CPTZDll dll;
        bFind = find.FindNextFile();
        if (find.IsArchived()) // 文件而非文件夹
        {
            // 输出这个文件:
            XTRACE("路径:%s/n" , find.GetFilePath());
            if ( dll.OpenPlugIn(find.GetFilePath()) ) // 如果是云台插件,则枚举出它的协议
            {
                CPlugInInfo info;
                const char ** ppProtocols = dll.GetSupportProtocols();
                if (!ppProtocols)
                {
                    continue ;
                }
                while (0 != *ppProtocols)
                {
                    string _string = *ppProtocols;
                    info.Protocols.push_back(_string);
                    ppProtocols ++ ;
                }
                info.szFullPath = find.GetFilePath();
                info.szFileName = find.GetFileName();
                PlugInList.push_back(info);
            }
        }
        dll.Close();
    }
    find.Close();
    // 下面是调试代码

    XTRACE("插件数量:%d/n" ,PlugInList.size());
    vector<CPlugInInfo>::iterator it = PlugInList.begin();
    for (; it != PlugInList.end(); it++ )
    {
        XTRACE("文件名:%s----》文件路径:%s/n" , (*it).szFileName.c_str(), (*it).szFullPath.c_str());
    }
}
4.在对话框工程的窗体中加入两个成员变量:
CPTZDll         m_PTZdll          ; // dll加载对象
CPTZInterface*  m_PTZInterface    ; // 正在使用的插件协议对象
用户选择一个插件:BOOL bReturn = m_PTZdll.OpenPlugIn(m_strProtocolPath);
然后根据该插件选择一个协议:m_PTZInterface = m_PTZdll.CreatePTZInstance(m_strProtocolName);    // 参数是一个表示协议的字符串
这时我们可以利用这个m_PTZInterface对象来执行各种操作。
说明:m_PTZInterface的类“CPTZInterface”是一个抽象类,由此派生出各种各样的协议。
其定义如下:
//PTZInterface.h
//iWise DVR PTZ Interface define header file.

#pragma once

#include "PTZDef.h"
#include "ComParam.h"

class CPTZInterface
{
public :
virtual void SetPTZSettings(int nComPort, int nAddrID, const ComParam & param) = 0 ;
virtual void Move(MOVEMENT_DIR nDirection) = 0 ;
virtual void Action(int nActionID, int param) = 0 ;
virtual int SetSpeed(int nSpeedTrgID, int nSpeed) = 0 ;
virtual void DeviceSwitch(int nDeviceID, bool bSwitchOn) = 0 ;
virtual int SetPreset(int nIndex) = 0 ;
virtual int CallPreset(int nIndex) = 0 ;
virtual void Reset() = 0 ;
virtual int SelfCheck() = 0 ;
virtual int GetCaps(int nCapID, void * lParam) = 0 ;
virtual int ExtendOp(int nExOpID, void * lParam) = 0 ;
virtual void Destroy() = 0 ;
};

 

 

参考文档:

云台控制协议插件开发文档

本文档描述有关 iWise-DVR 云台控制协议插件的程序结构、设计原理、插件接口等重要信息, iWisePTZTest 为插件的测试程序(插件也可以直接放到 iWise-DVR 环境中测试)。本文档以下出现的“ DVR ”均指“ iWise-DVR ”,本文档适用于使用串口的云台控制设备的基于 DVR 的云台控制协议插件的开发。

插件工作原理

云台控制有多种不同的控制协议, DVR 提供统一的插件接口以支持不同的云台控制协议,通过协议插件来进行对云台的控制操作。 DVR 启动时会扫描 Plug-Ins 目录里面的所有文件名以“ ptz ”开头的动态连接库( DLL ),并判断这些 DLL 是否为有效的云台控制协议插件。 DVR 定义了一套云台控制的基本操作,插件必须提供这些操作的具体实现,当用户要对云台进行控制时, DRV 最终将调用插件的方法来控制云台,插件需要根据协议特征产生相应的指令数据并向串口设备发送这些指令。同时 DVR 提供向串口发送数据的统一的方法。

云台控制协议插件接口

DVR 定义 class CPTZInterface 作为云台控制协议接口,该类是接口的核心,插件必须使用 CPTZInterface 派生一个类并实现所有的方法。 CPTZInterface 类详细定义如下:

class CPTZInterface

{

public:

      virtual void SetPTZSettings(int nComPort, int nAddrID,

const ComParam & param) = 0;

      virtual void Move(MOVEMENT_DIR nDirection) = 0;

      virtual void Action(int nActionID, int param) = 0;

      virtual int SetSpeed(int nSpeedTrgID, int nSpeed) = 0;

      virtual void DeviceSwitch(int nDeviceID, bool bSwitchOn) = 0;

      virtual int SetPreset(int nIndex) = 0;

      virtual int CallPreset(int nIndex) = 0;

      virtual void Reset() = 0;

      virtual int SelfCheck() = 0;

      virtual int GetCaps(int nCapID, void * lParam) = 0;

      virtual int ExtendOp(int nExOpID, void * lParam) = 0;

      virtual void Destroy() = 0;

};

SetPTZSettings 函数设置云台设备所在的串口号、地址码及通信参数。

nComPort - 云台设备所在串口号

nAddrID  - 设备地址码

param    - 通信参数,ComParam结构的引用(请参考《iWise-DVR环境串口操作接口使用说明》)。

 

Move 函数控制云台在各方向上的运动。

nDirection–运动方向,由MOVEMENT_DIR定义

enum MOVEMENT_DIR

{

      MD_STOP,         // 停止运动

      MD_LEFT,          // 向左

      MD_RIGHT,        // 向右

      MD_UP,            // 向上

      MD_DOWN,          // 向下

      MD_LEFT_UP,       // 左上

      MD_LEFT_DOWN,    // 左下

      MD_RIGHT_UP,      // 右上

      MD_RIGHT_DOWN,    // 右下

};

 

Action 函数控制云台摄像机的动作。

nActionID–动作定义

ACTION_ZOOM 缩放,param = 0时为缩小 param = 1时为放大

ACTION_ZOOM_STOP 停止缩放

ACTION_FOCUS 聚焦,param = 0时为往近处聚焦 1为往远处

ACTION_FOCUS_STOP 停止聚焦

ACTION_AUTO_FOCUS 自动聚焦

ACTION_AUTO_FOCUS_STOP 停止自动聚焦

ACTION_AUTO_SCAN 自动扫描

ACTION_AUTO_SCAN_STOP 停止自动扫描

 

SetSpeed 设置云台运动或摄像机动作的速度。

nSpeedTrgID–欲设置速度的目标

SPEED_TRG_PAN 水平方向运动

SPEED_TRG_TILT 垂直方向运动

ACTION_AUTO_SCAN 自动扫描

ACTION_AUTO_SCAN_STOP 停止自动扫描

nSpeed     –速度值 (0x00-0x40)

如果成功,该函数返回设置后的速度,否则返回0。

 

DeviceSwitch 函数提供打开、关闭某设备的功能。

nDeviceID–设备ID定义

DEVICE_CAMERA 照相机

DEVICE_LIGHT 灯光

DEVICE_RAINBRUSH 雨刷

DEVICE_IRIS 镜头光圈

bSwitchOn–true 为打开设备 false 为关闭设备

 

SetPreset 函数设置预置点。

nIndex–预置点索引号,从1开始,使用GetCaps 取得设备可以支持的预置点总数。

返回0为成功,否则失败

 

CallPreset 函数调用预置点,将云台摄象机移动到指定预置点位置。

nIndex–预置点索引号,对使用SetPreset设置成功的索引号有效。

返回0为成功,否则失败

 

Reset 函数使云台摄象机设备恢复到初始状态。

 

SelfCheck 函数使云台摄象机设备自检。

返回0为成功,否则失败

 

GetCaps 函数可以取得设备所能提供的能力。

nCapID–欲取得的能力的ID定义,请参考以下值

GC_BASAL 取得基本能力, lParam为指向PTZBasalCaps 结构的指针。

lParam–指向特定能力结构的指针。

返回0为成功,否则失败

 

ExtendOp 函数对CPTZInterface接口提供扩展功能。

nExOpID–扩展功能ID定义。

lParam–根据不同的nExOpID有不同的意义。

该函数目前未使用

 

Destroy 函数负责销毁自身对象。

销毁方式必须和 CreatePTZInstance 的创建方式一致

 

设备基本能力结构定义:

struct PTZBasalCaps

{

int nSize;                  // PTZBasalCaps结构的大小

int nPresetCount;           // 支持预置点的个数

int nMinSpeed;              // 云台移动速度最小值

int nMaxSpeed;              // 云台移动速度最大值

bool bAutoScanSupported;   // 是否支持自动扫描

}

 

插件DLL接口约定

插件 DLL 在实现了 CPTZInterface 的具体实现方法之后,必须提供一致的构建和销毁 CPTZInterface 对象的方法,因此必须导出如下函数:

        CPTZInterface * __declspec(dllexport) CreatePTZInstance (

const char * pszProtocolsName);

pszProtocolsName – 协议索名称,该名称由 GetSupportProtocols 函数取得。

该函数构造一个协议接口对象并返回其指针,该函数创建的对象必须由 CPTZInterface::Destroy() 函数自销毁。为安全起见,插件在卸载时也可以销毁使用者没有销毁的所有对象。

        const char ** __declspec(dllexport) GetSupportProtocols ();

nProtocols – 支持的协议数目

该函数返回插件所支持的协议名称列表指针

 

GetSupportProtocols 代码示例:

char * szProtocol[] = {

    "PELCO-D",

    "PELCO-P",

    0          // 必须以0表示结束

};

 

const char ** __declspec(dllexport) GetSupportProtocols()

{

    return (const char **)szProtocol;

}

 

以上代码表示该插件支持 "PELCO-D"、"PELCO-P"两种协议。

class CPTZInterface 声明在 ptzInterface.h 中。

有关串口指令的发送 请参考《iWise-DVR环境串口操作接口使用说明》。

1.创建项目,“Visual C++项目”--->“Win32”--->“Win32 项目”--->“DLL”,选中“导出符号”,(VS2003平台),按源码目录组织规范设置项目,参考我的blog 2004.12其中文章。
2.添加链接库导出定义文件:****.def,加入到项目中。
; ptzPelco-D.def : 声明 DLL 的模块参数。
 
LIBRARY       "ptzPelco-D"
 
EXPORTS
    ; 此处可以是显式导出
   
CreatePTZInstance  PRIVATE
ProtocolNames   DATA
说明:上面分别是导出变量和导出函数。
3.把工程默认的导出变量和导出函数删除,默认导出的类也删除(头文件可删空)。
4.在DllMain所在的cpp文件添加定义:
const char * ProtocolNames[] =
{
     "PELCO-D" ,
     "PELCO-P" ,
     0 // 必须以0表示结束
};

CPTZInterface * __stdcall CreatePTZInstance( const char * pszProtocolsName)
{
     if ( 0 == strcmp(pszProtocolsName, ProtocolNames[ 0 ]))
    {
          return new CPelco_D;     // 看下面的定义!
    }

     if ( 0 == strcmp(pszProtocolsName, ProtocolNames[ 1 ]))
    {
          return new CPelco_P;     // 看下面的定义!
    }

     return 0 ;
}
5.从接口派生出一个类,定义如下:
class CPelco_P: public CPTZInterface { …… }
注意上面的 CreatePTZInstance 函数,这样就达到了创建这个协议对象的目的了。
6.说明:创建的对象用该类的Destroy()销毁,由使用者调用。
7.转载请保留完整文档,天衣有缝原创,2004.12.24凌晨0:05,深圳南山科技园
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值