首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

2.5  云台控制方案

云台是一种硬件设备,用于调整摄像头的位置。通常,摄像头安装在云台之上,通过云台的转动实现摄像头水平、垂直方向的改变,从而实现对监控位置的调整。云台除了能够对摄像头进行转动,还可以控制摄像头的焦聚、光圈和变倍等,这样可以调整摄像头的捕捉范围和清晰度。本节将向您介绍有关云台控制方面的知识。

2.5.1  云台设备安装

云台通常是与摄像头结合在一起的,摄像头提供额外的线路用于云台设备与计算机的连接。该线路通常以Com端口的形式连接计算机,在线路的一端连接有云台控制转换器,如图2.47显示。

图2.47  云台控制转换器

云台控制转换器转换器是以Com端口的形式连接计算机的,图2.48显示了云台控制转换器与计算机的连接。

图2.48  云台控制转换器连接计算机

2.5.2  云台控制分析

云台是通过云台解码器与计算机串口或并口相连的,程序是通过向云台解码器发送指令来实现云台控制的。这里的指令是由云台控制协议确定的。不同的厂家,云台控制协议也不尽相同。以Pelco-D2400为例,其命令格式由7个字节构成。第一个字节为同步字节,始终为FFH,第2个字节为地址码,也就是摄像头的逻辑地址号,范围在00H到FFH之间,是在安装摄像头时手动设置的,该值一定要正确,否则命令不会执行。第3、4个字节表示指令码,即执行哪项操作,例如,向上、下、左、右移动摄像头等。第5、6个字节表示数据码,用于指定摄像头的水平、垂直方向移动速度。第7个字节为校验码,它是由第2、3、4、5、6个字节数据之和与100H取模获得的。

此外,在执行某一命令后,应执行停止命令,否则命令执行的动作会一直执行。例如,将摄像头向上移动。如果不发送停止命令,摄像头就会一直向上移动。在Pelco-D2400协议的停止命令中,第一个字节为FFH,第2个字节为地址码,第3、4、5、6个字节为00F,第7个字节为第2个字节数据与100H的模。

只要知道了控制命令,就可以通过向串口发送这些命令来控制云台了。例如,下面的代码实现了云台的向下移动。

void CCloudMsgDlg::OnDown()

{

   //向下移动

   unsigned char data[7]= {0xff,0x02,0x00,0x10,0x00,0xff,0x12};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =7;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<7; i++)

   {

       SafeArrayPutElement(pSafe,&i,&data[i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

   //停止移动

   unsigned char stopdata[7]= {0xff,0x02,0x00,0x00,0x00,0x00,0x02};

   for (i = 0; i<7; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&stopdata[i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

在设计云台控制程序时,为了使程序能够灵活地控制云台,不要向云台解码器发送固定的指令,而应提供接口让用户针对不同的云台控制协议自行设置云台控制码。下面笔者以一个具体实例介绍云台控制程序的设计。效果如图2.49、图2.50所示。

图2.49  云台控制效果图

图2.50  控制码设置效果图

程序设计具体步骤如下:

*  实例位置光盘\mr\2\2.5\2.5.2\01

(1)设计一个INI文件,其中存储云台控制的各种参数。

[端口设置]

端口号 =1

环境设置 =2400,n,8,1

[使用字节数]

字节数 =7

[控制项]

控制数 =13

[1]   --上

字节1 =0xff

字节2 =0x2

字节3 =0x0

字节4 =0x8

字节5 =0x0

字节6 =0xff

字节7 =0x10

字节8 =

[2]   --下

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x10

字节5 = 0x00

字节6 = 0xff

字节7 = 0x12

字节8 =

[3]   --左

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x04

字节5 = 0xff

字节6 = 0x00

字节7 = 0x06

字节8 =

[4]   --右

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x02

字节5 = 0xff

字节6 = 0x00

字节7 = 0x04

字节8 =

[5]   --减小聚焦

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x80

字节5 = 0x00

字节6 = 0x00

字节7 = 0x82

字节8 =

[6]   --增加聚焦

字节1 = 0xff

字节2 = 0x02

字节3 = 0x01

字节4 = 0x00

字节5 = 0x00

字节6 = 0x00

字节7 = 0x03

字节8 =

[7]   --减小对焦

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x20

字节5 = 0x00

字节6 = 0x00

字节7 = 0x22

字节8 =

[8]   --增加对焦

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x40

字节5 = 0x00

字节6 = 0x00

字节7 = 0x42

字节8 =

[9]   --减小光圈

字节1 = 0xff

字节2 = 0x02

字节3 = 0x02

字节4 = 0x00

字节5 = 0x00

字节6 = 0x00

字节7 = 0x04

字节8 =

[10]   --增加光圈

字节1 = 0xff

字节2 = 0x02

字节3 = 0x04

字节4 = 0x00

字节5 = 0x00

字节6 = 0x00

字节7 = 0x06

字节8 =

[11]   --关闭雨刷

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x00

字节5 = 0x00

字节6 = 0x00

字节7 = 0x00

字节8 =

[12]   --打开雨刷

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x00

字节5 = 0x00

字节6 = 0x00

字节7 = 0x00

字节8 =

[13]   --复位

字节1 = 0xff

字节2 = 0x02

字节3 = 0x00

字节4 = 0x00

字节5 = 0x00

字节6 = 0x00

字节7 = 0x02

字节8 =

(2)创建一个基于对话框的工程,向对话框中添加按钮控件。

(3)在对话框中鼠标右键单击,在弹出的快捷菜单中选择“Insert ActiveX Control”菜单项,打开“Insert ActiveX Control”窗口,如图2.51、图2.52所示。

图2.51  快捷菜单

图2.52  “Insert ActiveX Control”窗口

(4)在“Insert ActiveX Control”窗口中选择“Microsoft Communications Control”选项,将MSCom控件导入到对话框中,如图2.53所示。

图2.53  云台控制对话框

(5)打开类向导窗口,选择“Member Variables”选项卡,为控件命名,如图2.54所示。

图2.54  成员变量窗口

(6)向对话框类中添加成员变量,记录Com端口信息及云台控制码。

unsigned char (*m_pData) [MAXNUM];  //存储云台控制码

int      m_Len ;                        //云台协议使用的字节数

int      m_ActoinCount;                //云台控制动作数

int      m_Port;                        //Com端口

CString  m_Setting;                    //环境

(7)向对话框中添加控制云台的各个方法。

//向上移动

void CCloudMsgDlg::OnUp()

{

// unsigned char data[8]= {0xff,0x02,0x00,0x08,0x00,0xff,0x10};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[0][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//停止移动

void CCloudMsgDlg::OnReset()

{

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   //unsigned char stopdata[7]= {0xff,0x02,0x00,0x00,0x00,0x00,0x02};

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[12][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//向下移动

void CCloudMsgDlg::OnDown()

{

   // unsigned char data[7]= {0xff,0x02,0x00,0x10,0x00,0xff,0x12};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[1][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

 }

//向左移动

void CCloudMsgDlg::OnLeft()

{

   //unsigned char data[7]= {0xff,0x02,0x00,0x04,0xff,0x00,0x06};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[2][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//向右移动

void CCloudMsgDlg::OnRight()

{

   //unsigned char data[7]= {0xff,0x02,0x00,0x02,0xff,0x00,0x04};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[3][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//增加聚焦

void CCloudMsgDlg::OnInFoci()

// unsigned char data[7]= {0xff,0x02,0x01,0x00,0x00,0x00,0x03};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[5][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//减小聚焦

void CCloudMsgDlg::OnReFoci()

{

   //unsigned char data[7]= {0xff,0x02,0x00,0x80,0x00,0x00,0x82};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[4][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//倍长增

void CCloudMsgDlg::OnInLen()

{

   //unsigned char data[7]= {0xff,0x02,0x00,0x40,0x00,0x00,0x42};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[7][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//倍长减

void CCloudMsgDlg::OnReLen()

{

   //unsigned char data[7]= {0xff,0x02,0x00,0x20,0x00,0x00,0x22};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[6][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//光圈减

void CCloudMsgDlg::OnReAperture()

{

   //unsigned char data[7]= {0xff,0x02,0x02,0x00,0x00,0x00,0x04};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[8][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//光圈增

void CCloudMsgDlg::OnInAperture()

{

// unsigned char data[8]= {0xff,0x02,0x04,0x00,0x00,0x00,0x06};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[9][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//雨刷减

void CCloudMsgDlg::OnReBrush()

{

   //unsigned char data[7]= {0xff,0x00,0x00,0x00,0x00,0x00,0x00};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[10][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

//雨刷增

void CCloudMsgDlg::OnInBrush()

{

// unsigned char data[7]= {0xff,0x00,0x00,0x00,0x00,0x00,0x00};

   VARIANT vt;

   SAFEARRAY* pSafe;

   SAFEARRAYBOUND band;

   band.cElements =m_Len;

   band.lLbound = 0;

   pSafe = SafeArrayCreate(VT_UI1,1,&band);

   for (long i = 0; i<m_Len; i++)

   {

       SafeArrayPutElement(pSafe,&i,(void*)&m_pData[11][i]);

   }

   vt.vt= VT_ARRAY |VT_UI1;

   vt.parray = pSafe;

   m_Com.SetOutput((COleVariant)vt);

}

(8)从CButton类派生一个子类CStageButton,目的是在按下按钮时不停地控制云台,在释放按钮时停止对云台的控制。CStageButton类声明代码如下:

//按钮控制类型,上 下 左 右 自动,焦聚减,焦聚增,倍长减,倍长增,光圈减,光圈增,雨刷减,雨刷增

enum  ControlType {ctUp,ctDown,ctLeft,ctRight,ctAuto,ctReFoci,

      ctInFoci,ctReLen,ctInLen,ctReAperture,ctInAperture,ctReBrush,ctInBrush};

class CStageButton : public CButton

{

// Construction

public:

   CStageButton();

// Attributes

public:

   ControlType m_Type;

// Operations

public:

// Overrides

   // ClassWizard generated virtual function overrides

   //{{AFX_VIRTUAL(CStageButton)

   //}}AFX_VIRTUAL

// Implementation

public:

   virtual ~CStageButton();

   // Generated message map functions

protected:

   //{{AFX_MSG(CStageButton)

   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

   afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

   //}}AFX_MSG

   DECLARE_MESSAGE_MAP()

};

CStageButton类实现代码如下:

//在鼠标按钮时根据按钮的类型标识,调用不同的方法

void CStageButton::OnLButtonDown(UINT nFlags, CPoint point)

{

   switch (m_Type)

   {

   case ctUp:

       {

              ((CCloudMsgDlg*)GetParent())->OnUp();

              break;

       }

   case ctDown:

       {

              ((CCloudMsgDlg*)GetParent())->OnDown();

              break;

       }

   case ctLeft:

       {

              ((CCloudMsgDlg*)GetParent())->OnLeft();

              break;

       }

   case ctRight:

       {

              ((CCloudMsgDlg*)GetParent())->OnRight();

              break;

       }

   case ctInFoci:

       {

              ((CCloudMsgDlg*)GetParent())->OnInFoci();

              break;

       }

   case ctReFoci:

       {

              ((CCloudMsgDlg*)GetParent())->OnReFoci();

              break;

       }

   case ctInLen:

       {

              ((CCloudMsgDlg*)GetParent())->OnInLen();

              break;

       }

   case ctReLen:

       {

              ((CCloudMsgDlg*)GetParent())->OnReLen();

              break;

       }

   case ctInAperture:

       {

              ((CCloudMsgDlg*)GetParent())->OnInAperture();

              break;

       }

   case ctReAperture:

       {

              ((CCloudMsgDlg*)GetParent())->OnReAperture();

              break;

       }

   case ctInBrush:

       {

              ((CCloudMsgDlg*)GetParent())->OnInBrush();

              break;

       }

   case ctReBrush:

       {

              ((CCloudMsgDlg*)GetParent())->OnReBrush();

              break;   

       }

   default:

       {

              //......

              break;

       }

   }

   CButton::OnLButtonDown(nFlags, point);

}

//在释放鼠标按钮时停止对云台的控制

void CStageButton::OnLButtonUp(UINT nFlags, CPoint point)

{

   ((CCloudMsgDlg*)GetParent())->OnReset();

   CButton::OnLButtonUp(nFlags, point);

}

(9)在对话框初始化时从INI文件中读取云台参数信息,并设置Com端口。

BOOL CCloudMsgDlg::OnInitDialog()

{

   CDialog::OnInitDialog();

   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);

   if (pSysMenu != NULL)

   {

       CString strAboutMenu;

       strAboutMenu.LoadString(IDS_ABOUTBOX);

       if (!strAboutMenu.IsEmpty())

       {

              pSysMenu->AppendMenu(MF_SEPARATOR);

              pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

       }

   }

   SetIcon(m_hIcon, TRUE);   

   SetIcon(m_hIcon, FALSE);

   m_ButtonUp.m_Type    = ctUp;

   m_ButtonDown.m_Type  = ctDown;

   m_ButtonLeft.m_Type  = ctLeft;

   m_ButtonRight.m_Type = ctRight;

   m_InFoci.m_Type      = ctInFoci;

   m_ReFoci.m_Type      = ctReFoci;

   m_InLen.m_Type       = ctInLen;

   m_ReLen.m_Type       = ctReLen;

   m_InAperture.m_Type  = ctInAperture;

   m_ReAperture.m_Type  = ctReAperture;

   m_InBrush.m_Type     = ctInBrush;

   m_ReBrush.m_Type     = ctReBrush;

   m_Len = GetPrivateProfileInt("使用字节数","字节数",8,"./stage.ini");

   m_ActoinCount = GetPrivateProfileInt("控制项","控制数",13,"./stage.ini");

   m_pData = new  unsigned char[m_ActoinCount][MAXNUM];

   m_Port = GetPrivateProfileInt("端口设置","端口号",1,"./stage.ini");

   GetPrivateProfileString("端口设置","环境设置","9600,n,8,1"

,m_Setting.GetBuffer(0),MAX_PATH,"./stage.ini");

   int data;

   char buff[20] = {0};

   char var[20] = {0};

   for (int i = 0 ; i<m_ActoinCount ; i++)

       for (int j = 0 ; j <m_Len; j++)

   {

       char section[20] = "字节";

       itoa(i+1,var,10);

       itoa(j+1,buff,10);

       strcat(section,buff);

       data = GetPrivateProfileInt(var,section,0,"./stage.ini");

       m_pData[i][j] = data;

   }

   //设置端口信息,并打开端口

   m_Com.SetSettings(m_Setting);

   m_Com.SetOutBufferSize(512);

   m_Com.SetCommPort(m_Port);

   m_Com.SetSThreshold(0);

   m_Com.SetPortOpen(TRUE);

   return TRUE; 

}

(10)在工作区的“Class View”选项卡中鼠标右键单击根节点,在弹出的快捷菜单中选择“New Form”菜单项,打开“NewForm”窗口,如图2.55、图2.56所示。

图2.55  新建对话框快捷菜单

图2.56  “NewForm”窗口

(11)在“Name”编辑框中输入对话框的类名称,单击“OK”按钮创建对话框。

(12)在新建立的对话框中添加按钮、组合框、编辑框、静态文本等控件,如图2.57所示。

图2.57  控制码设置对话框

(13)按“Ctrl+W”组合键打开类向导窗口,选择“Member Variables”选项卡,为对话框中的控件命名,如图2.58所示。

图2.58  控制码设置对话框控件命名窗口

(14)向对话框中添加如下成员变量。

CCloudMsgDlg* m_pMain;     //主窗口指针

CEdit*        m_pEdit[8];  //控件数组

(15)在对话框初始化时为控件和成员变量赋值。

BOOL CControlForm::OnInitDialog()

{

   CDialog::OnInitDialog();

  

   m_pEdit[0] = &m_Byte1;

   m_pEdit[1] = &m_Byte2;

   m_pEdit[2] = &m_Byte3;

   m_pEdit[3] = &m_Byte4;

   m_pEdit[4] = &m_Byte5;

   m_pEdit[5] = &m_Byte6;

   m_pEdit[6] = &m_Byte7;

   m_pEdit[7] = &m_Byte8;

   m_pMain = (CCloudMsgDlg*)AfxGetApp()->GetMainWnd();

  

   m_Port.SetCurSel(m_pMain->m_Port-1);

   m_Setting.SetWindowText(m_pMain->m_Setting);

   char data[20];

   itoa(m_pMain->m_ActoinCount,data,10);

   m_ActionNum.SetWindowText(data);

  

   char buff[20];

   itoa(m_pMain->m_Len,buff,10);

   m_ByteNum.SetWindowText(buff);

  

   return TRUE; 

}

(16)处理“设置端口”按钮的单击事件,将信息保存到INI文件中,并更新Com端口信息。

void CControlForm::OnPortset()

{

   //设置端口信息

   CString port;

   m_Port.GetWindowText(port);

   if (!port.IsEmpty())

   {

       m_pMain->m_Port = m_Port.GetCurSel()+1;

       port.Format("%i",m_pMain->m_Port);

       WritePrivateProfileString("端口设置","端口号",port,"./stage.ini");

       m_pMain->m_Com.SetPortOpen(FALSE);

       m_pMain->m_Com.SetCommPort(m_pMain->m_Port);

       m_pMain->m_Com.SetPortOpen(TRUE);

   }

   CString setting;

   m_Setting.GetWindowText(setting);

   if (!setting.IsEmpty())

   {

       m_pMain->m_Setting = setting;

       WritePrivateProfileString("端口设置","环境设置",setting,"./stage.ini");

       m_pMain->m_Com.SetPortOpen(FALSE);

       m_pMain->m_Com.SetSettings(setting);

       m_pMain->m_Com.SetPortOpen(TRUE);

   }

}

(17)处理“控制动作”组合框选项改变时的事件,显示相应的云台控制码。

void CControlForm::OnSelchangeActions()

{

   int index  = m_Actions.GetCurSel();

   if (index != -1)

   {

       for (int j = 0 ; j<m_pMain->m_Len; j++)

              for (int i = 0 ; i< m_pMain->m_ActoinCount; i++)

              {

                 char buff[20] = {0};

                 char prex[20]= "0x";

                 itoa(m_pMain->m_pData[index][j],buff,16);

                 strcat(prex,buff);

                 m_pEdit[j]->SetWindowText(prex);  

              }

   }

}

(18)处理“设置”按钮的单击事件,将云台控制码保存到INI文件中。

void CControlForm::OnCmdset()

{

   //设置控制码

   int index  = m_Actions.GetCurSel();

   if (index != -1)

   {

       for (int j = 0 ; j<m_pMain->m_Len; j++)

       {

              char data[20];

              m_pEdit[j]->GetWindowText(data,20);

             

              char* stop;

              int x = strtol(data,&stop,16);

              m_pMain->m_pData[index][j] = strtol(data,&stop,16);

              char sec[20] = {0};

              char key[20] = "字节";

              char num[10] = {0};

              itoa(j+1,num,10);

              strcat(key,num);

              itoa(index+1,sec,10);

              WritePrivateProfileString(sec,key,data,"./stage.ini");

       }

   }

   //设置动作数和协议使用的字节数

   CString num;

   m_ByteNum.GetWindowText(num);

  

   if (!num.IsEmpty())

   {

       WritePrivateProfileString("使用字节数","字节数",num,"./stage.ini");

   }

  

   CString actions;

   m_ActionNum.GetWindowText(actions);

   if (! actions.IsEmpty())

   {

       WritePrivateProfileString("控制项","控制数",actions,"./stage.ini");    

   }

   CDialog::OnCancel();

}

2.5.3  定时广角监控方案

在开发监控系统时,可能要求摄像头捕捉的范围更广泛,这样就不能使摄像头始终捕捉一个方向,而是时时进行全方位的捕捉。为此,在设计程序时应当考虑人为控制和系统自动控制两个方面。当有人值班时,他可以控制摄像头捕捉的方位,当无人值班时,系统要完成各个方位的自动捕捉。

实际上,系统实现的关键是对云台的自动控制。当无人值班时,可以让摄像头由左向右、由上到下等顺序按一定速度转动。这里需要注意的是转动的速度要适宜,即不要太快,也不要太慢。如果太快的话,捕捉的画面质量较差,起不到监控的作用了。如果太慢的话,当有人在摄像头监控范围的某一个区域进行非法活动时,摄像头可能不能及时捕捉,使得监控形同虚设了。

为了在系统自动捕捉时用户能够进行界面操作。笔者通过创建一个线程来执行捕捉的动作,当用户需要停止系统自动捕捉,只要终止线程就可以了。效果如图2.59所示。

图2.59  定时广角监控方案

程序设计具体步骤如下:

*  实例位置光盘\mr\2\2.5\2.5.3\01

(1)创建一个基于对话框的工程,向对话框中添加标签、按钮、日期等控件,如图2.60所示。

图2.60  对话框资源

(2)向对话框类中添加如下成员变量。

//成员变量

unsigned char (*m_pData) [100];  //端口数据

int       m_Len ;                    //云台协议使用的字节数

int       m_ActoinCount;            //云台控制动作数

int       m_Port;                    //端口号

CString  m_Setting;                //端口信息

BOOL      m_Tail;                   //是否开始监控

HANDLE   m_hThread;                //线程句柄

(3)在对话框初始化时从INI文件中读取基础信息。

BOOL CTimeDIYDlg::OnInitDialog()

{

   CDialog::OnInitDialog();

   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);

   if (pSysMenu != NULL)

   {

      CString strAboutMenu;

       strAboutMenu.LoadString(IDS_ABOUTBOX);

       if (!strAboutMenu.IsEmpty())

       {

              pSysMenu->AppendMenu(MF_SEPARATOR);

              pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

       }

   }

   SetIcon(m_hIcon, TRUE);     

   SetIcon(m_hIcon, FALSE);  

   CTime time = CTime::GetCurrentTime();

   m_Time.SetTime(&time);

   m_Len = GetPrivateProfileInt("使用字节数","字节数",8,"./stage.ini");

   m_ActoinCount = GetPrivateProfileInt("控制项","控制数",13,"./stage.ini");

   m_pData = new  unsigned char[m_ActoinCount][100];

   m_Port = GetPrivateProfileInt("端口设置","端口号",1,"./stage.ini");

   GetPrivateProfileString("端口设置","环境设置

","9600,n,8,1",m_Setting.GetBuffer(0),MAX_PATH,"./stage.ini");

   int data;

   char buff[20] = {0};

   char var[20] = {0};

   for (int i = 0 ; i<m_ActoinCount ; i++)

       for (int j = 0 ; j <m_Len; j++)

   {

       char section[20] = "字节";

       itoa(i+1,var,10);

       itoa(j+1,buff,10);

       strcat(section,buff);

       data = GetPrivateProfileInt(var,section,0,"./stage.ini");

       m_pData[i][j] = data;

   }

   //设置端口信息,并打开端口

   m_Com.SetSettings(m_Setting);

   m_Com.SetOutBufferSize(512);

   m_Com.SetCommPort(m_Port);

   m_Com.SetSThreshold(0);

   m_Com.SetPortOpen(TRUE);

  

   if (VCAInitSdk(m_hWnd))

   {

       VCARegVidCapCallBack(0,CapCallBack);

       VCAOpenDevice(0,m_Panel.m_hWnd);

       VCAStartVideoPreview(0);

   } 

   m_Tail = FALSE;

  

   SetTimer(1,300,NULL);

   return TRUE; 

}

(4)处理对话框的WM_WINDOWPOSCHANGED消息,在对话框位置改变时更新视频预览窗口。

void CTimeDIYDlg::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos)

{

   CDialog::OnWindowPosChanged(lpwndpos);

   VCAUpdateOverlayWnd(m_Panel.m_hWnd);

   VCAUpdateVideoPreview(0,m_Panel.m_hWnd);

}

(5)编写线程函数,实现系统的自动监控。

DWORD WINAPI ThreadProc(LPVOID lpParameter )

{

   CTimeDIYDlg* pDlg = (CTimeDIYDlg*)lpParameter;

   while (true)

   {

       //进行云台控制

       VARIANT vt;

       SAFEARRAY* pSafe;

       SAFEARRAYBOUND band;

       band.cElements =pDlg->m_Len;

       band.lLbound = 0;

       pSafe = SafeArrayCreate(VT_UI1,1,&band);

       //向上

       for (long i = 0; i<pDlg->m_Len; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&pDlg->m_pData[0][i]);

       }

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       pDlg->m_Com.SetOutput((COleVariant)vt);

       Sleep(15000);

       //向左

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       for ( i = 0; i<pDlg->m_Len; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&pDlg->m_pData[2][i]);

       }

       pDlg->m_Com.SetOutput((COleVariant)vt);

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       for ( i = 0; i<pDlg->m_Len; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&pDlg->m_pData[1][i]);

       }

       Sleep(15000);

       //向下

       pDlg->m_Com.SetOutput((COleVariant)vt);

       //向右

       Sleep(15000);

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       for ( i = 0; i<pDlg->m_Len; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&pDlg->m_pData[3][i]);

       }

       pDlg->m_Com.SetOutput((COleVariant)vt);

       Sleep(15000);

   }

   return 0;

}

(6)处理“开始监控”按钮的单击事件,让系统自动监控。

void CTimeDIYDlg::OnStartTail()

{

   m_Tail = TRUE;

   //开始监控

   VCAStartVideoCapture(0,CAP_MPEG4_STREAM,MPEG4_AVIFILE_ONLY ,"C:\\WW.avi");

   DWORD threadID;

   m_hThread = ::CreateThread(NULL,0,ThreadProc,(LPVOID)this,0,&threadID);

}

(7)处理“停止监控”按钮的单击事件,停止系统自动监控。

void CTimeDIYDlg::OnStopTail()

{

   if (m_Tail==TRUE)

   {

       //停止运动

       VARIANT vt;

       SAFEARRAY* pSafe;

       SAFEARRAYBOUND band;

       band.cElements =m_Len;

       band.lLbound = 0;

       pSafe = SafeArrayCreate(VT_UI1,1,&band);

       for (long i = 0; i<m_Len; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&m_pData[12][i]);

       }

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       m_Com.SetOutput((COleVariant)vt);

       ::TerminateThread(m_hThread,0);

       //停止监控

       VCAStopVideoCapture(0);

       m_Tail = FALSE;

   }

}

(8)处理对话框的WM_TIMER消息,当指定的时间到达时,实现提供的自动监控。

void CTimeDIYDlg::OnTimer(UINT nIDEvent)

{

   CTime time= CTime::GetCurrentTime();

  

   CTime strtime;

   m_Time.GetTime(strtime);

   if (time==strtime)

   {

       OnStartTail() ;

       KillTimer(1);

   }

   CDialog::OnTimer(nIDEvent);

}

(9)在对话框关闭时停止摄像头的转动。

void CTimeDIYDlg::OnCancel()

{

   delete [] m_pData;

   VCACloseDevice(0);

   VCAUnInitSdk(); 

   OnStopTail();

2.5.4  远程云台控制方案

在开发较大型的监控系统时,由于监控设备的物理位置比较分散,因此通常需要通过网络来对监控设备进行控制。在程序中实现云台远程控制比较容易,可以采用客户/服务器的模式来实现,客户端将指令发送给服务器,服务器在接收到指令后执行相应的动作。效果如图2.61、图2.62所示。

图2.61  服务器端效果图

图2.62  客户端效果图

下面笔者结合实例介绍远程云台控制的实现。服务器端程序设计步骤如下:

*  实例位置光盘\mr\2\2.5\2.5.4\01

(1)创建一个基于对话框的应用程序,在对话框中添加标签、按钮、编辑框等控件,并导入ActiveX控件——MSComm,如图2.63所示。

图2.63  服务器端对话框资源

(2)在“stdafx.h”头文件中引用一些头文件,目的是使用套接字相关函数。

#include "winsock2.h"

#include <afxsock.h>

#pragma comment (lib,"ws2_32.lib") //连接ws2_32.lib库

(3)在应用程序初始化时初始套接字。

//初始化套接字

WSADATA data;

AfxSocketInit(&data);

(4)在对话框初始化时创建面向无连接套接字,并且将其绑定到本机的指定端口上。

BOOL CCloudMsgDlg::OnInitDialog()

{

   CDialog::OnInitDialog();

   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);

   if (pSysMenu != NULL)

   {

       CString strAboutMenu;

       strAboutMenu.LoadString(IDS_ABOUTBOX);

       if (!strAboutMenu.IsEmpty())

       {

              pSysMenu->AppendMenu(MF_SEPARATOR);

              pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

       }

   }

   SetIcon(m_hIcon, TRUE);   

   SetIcon(m_hIcon, FALSE);

   CString IP;

   GetPrivateProfileString("端口设置","端口号","",IP.GetBuffer(0),

MAX_PATH,"./stage.ini");

   m_Port.SetWindowText(IP);

   CString setting;

   GetPrivateProfileString("端口设置","环境设置","",setting.GetBuffer(0),

MAX_PATH,"./stage.ini");

   m_Setting.SetWindowText(setting);

   //创建UDP套接字

   m_Client = socket(AF_INET,SOCK_DGRAM,0);

   //获取本机名称

   char name[MAX_PATH];

   memset(name,0,MAX_PATH);

   gethostname(name,MAX_PATH);

   hostent* pTent =  gethostbyname(name);

   sockaddr_in addr;

   addr.sin_family = AF_INET;

   addr.sin_addr =  (* (in_addr*)pTent->h_addr_list[0]) ;

   long port = 600;

   addr.sin_port = htons(port);

   if (bind(m_Client,(sockaddr*)&addr,sizeof(addr))==SOCKET_ERROR)

   {

       MessageBox("地址绑定错误");

   }

   WSAAsyncSelect(m_Client,m_hWnd,CM_READINFO,FD_READ|FD_WRITE);

   //设置端口信息,并打开端口

   m_Com.SetSettings("2400,n,8,1");

   m_Com.SetOutBufferSize(512);

   m_Com.SetCommPort(1);

   m_Com.SetSThreshold(0);

   m_Com.SetPortOpen(TRUE);

   return TRUE; 

}

(5)处理“设置”按钮的单击事件,设置Com端口信息。

void CCloudMsgDlg::OnPortset()

{

   CFileDialog fDlg(TRUE,NULL,NULL);

   fDlg.DoModal();

   CString IP;

   m_Port.GetWindowText(IP);

   WritePrivateProfileString("端口设置","端口号",IP.GetBuffer(0),"./stage.ini");

  

   CString setting;

   m_Setting.GetWindowText(setting);

   WritePrivateProfileString("端口设置","环境设置

",setting.GetBuffer(0),"./stage.ini");

   m_Com.SetPortOpen(FALSE); 

   m_Com.SetSettings(setting);

   m_Com.SetOutBufferSize(512);

   m_Com.SetCommPort(atoi(IP));

   m_Com.SetSThreshold(0);

   m_Com.SetPortOpen(TRUE);  

}

(6)向对话框中添加一个消息映射宏,在其消息处理函数中读取客户端发来的指令,并将其发送到Com端口中。

ON_MESSAGE(CM_READINFO,OnReadInfo)

//消息处理函数

void CCloudMsgDlg::OnReadInfo(WPARAM wp, LPARAM lp)

{

   unsigned char data[MAX_PATH];

   memset(data,0,MAX_PATH);

   sockaddr_in  addr;

   int len = sizeof(addr);

   int ret = recvfrom(m_Client,(char*)data,MAX_PATH,0,(SOCKADDR*)&addr,&len);

   if (ret >0)

   {

       //从数据报中获取命令码的字节数

       int packagelen = data[1];

       VARIANT vt;

       SAFEARRAY* pSafe;

       SAFEARRAYBOUND band;

       band.cElements =packagelen;

       band.lLbound = 0;

       pSafe = SafeArrayCreate(VT_UI1,1,&band);

       //读取控制码

       for (long i = 0; i<packagelen; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&data[i+2]);

       }

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       m_Com.SetOutput((COleVariant)vt);

       //读取停止码

       for (i = 0; i<packagelen; i++)

       {

              SafeArrayPutElement(pSafe,&i,(void*)&data[packagelen+2+i]);

       }

       vt.vt= VT_ARRAY |VT_UI1;

       vt.parray = pSafe;

       Sleep(500);

       m_Com.SetOutput((COleVariant)vt);

   }

}

(7)在对话框关闭时关闭套接字。

void CCloudMsgDlg::OnCancel()

{

   closesocket(m_Client);

   CDialog::OnCancel();

}

客户端程序设计步骤如下:

*  实例位置光盘\mr\2\2.5\2.5.4\02

(1)创建一个基于对话框的工程,在对话框中添加按钮、群组框等控件,如图2.64所示。

图2.64  客户框对话框资源

(2)向对话框类中添加成员变量,记录云台参数及服务器信息。

//成员变量

unsigned char (*m_pData) [100];    //存储云台控制码

int      m_Len ;                 //云台协议使用的字节数

int      m_ActoinCount;           //云台控制动作数

int      m_Port;                 //端口

CString  m_Setting;                   //环境设置

SOCKET   m_Client;               //客户端套接字

int      m_SrvPort;               //服务器端口

CString  m_Server;               //服务器名称或IP

(3)在对话框初始化时创建套接字,绑定套接字地址,从INI文件中读取云台参数信息。

BOOL CCloudMsgDlg::OnInitDialog()

{

   CDialog::OnInitDialog();

   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);

   if (pSysMenu != NULL)

   {

       CString strAboutMenu;

       strAboutMenu.LoadString(IDS_ABOUTBOX);

       if (!strAboutMenu.IsEmpty())

       {

              pSysMenu->AppendMenu(MF_SEPARATOR);

              pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

       }

   }

   SetIcon(m_hIcon, TRUE);   

   SetIcon(m_hIcon, FALSE);

   //创建UDP套接字

   m_Client = socket(AF_INET,SOCK_DGRAM,0);

   //获取本机名称

   char name[MAX_PATH];

   memset(name,0,MAX_PATH);

   gethostname(name,MAX_PATH);

   hostent* pTent =  gethostbyname(name);

   sockaddr_in addr;

   addr.sin_family = AF_INET;

   addr.sin_addr = (* (in_addr*)pTent->h_addr_list[0]) ;

   addr.sin_port = htonl(60025);

   if (bind(m_Client,(sockaddr*)&addr,sizeof(addr))==SOCKET_ERROR)

   {

       MessageBox("地址绑定错误");

   }

   m_ButtonUp.m_Type    = ctUp;

   m_ButtonDown.m_Type  = ctDown;

   m_ButtonLeft.m_Type  = ctLeft;

   m_ButtonRight.m_Type = ctRight;

   m_InFoci.m_Type      = ctInFoci;

   m_ReFoci.m_Type      = ctReFoci;

   m_InLen.m_Type       = ctInLen;

   m_ReLen.m_Type       = ctReLen;

   m_InAperture.m_Type  = ctInAperture;

   m_ReAperture.m_Type  = ctReAperture;

   m_InBrush.m_Type     = ctInBrush;

   m_ReBrush.m_Type     = ctReBrush;

   m_Len = GetPrivateProfileInt("使用字节数","字节数",8,"./stage.ini");

   m_ActoinCount = GetPrivateProfileInt("控制项","控制数",13,"./stage.ini");

   m_pData = new  unsigned char[m_ActoinCount][100];

   m_Port = GetPrivateProfileInt("端口设置","端口号",1,"./stage.ini");

   GetPrivateProfileString("端口设置","环境设置","9600,n,8,1",

m_Setting.GetBuffer(0),MAX_PATH,"./stage.ini");

   int data;

   char buff[20] = {0};

   char var[20] = {0};

   for (int i = 0 ; i<m_ActoinCount ; i++)

       for (int j = 0 ; j <m_Len; j++)

   {

       char section[20] = "字节";

       itoa(i+1,var,10);

       itoa(j+1,buff,10);

       strcat(section,buff);

       data = GetPrivateProfileInt(var,section,0,"./stage.ini");

       m_pData[i][j] = data;

   }

   return TRUE; 

}

(4)向对话框中添加SendCmd方法,根据参数的不同,向服务器端发送不同的指令。

void CCloudMsgDlg::SendCmd(int CtrCmd,int StopCmd /*=12*/)

{

   sockaddr_in addr;

   addr.sin_family = AF_INET;

   addr.sin_addr.S_un.S_addr = inet_addr(m_Server);

   addr.sin_port = htons(m_SrvPort);

   //定义数据报的格式

   //--报的总长度(1个字节)--云台控制码字节数(1个字节)--控制码数据--停止码数据

   unsigned char * pPackage = new unsigned char [m_Len*2+2];

    memset(pPackage,0,m_Len+2+3);

   //填充数据报

   pPackage[0] = m_Len*2+2;

   pPackage[1] = m_Len  ;

   int index = 2;

   //填充控制码数据

   for (int i = 0 ; i<m_Len; i++,index++)

   {

       pPackage[index] = m_pData[CtrCmd][i];

   }

   //填充停止码数据

   for ( i = 0 ; i<m_Len; i++,index++)

   {

       pPackage[index] = m_pData[StopCmd][i];

   }

   //发送数据报

   sendto(m_Client,(char*)pPackage,(m_Len*2+2)*2,0,(sockaddr*)&addr,sizeof(addr));

   delete [] pPackage;  //equal to delete pPackage

}

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论