自制摄像头云台,并通过计算机LPT并口直接控制(二)
硬件准备
1,待监控对象若干(只、个、位),呵呵开个玩笑。
2,普通摄像头一个,电脑市场到处都是卖的,¥30~40左右一个,我这个买的时候号称1400万像素,要是真的话,我还是回去把我的佳能7D扔掉好了。
3,RC模型舵机两个,淘宝有的卖,10~20一个,因为我们只是控制摄像头转动,需要的力矩很小,普通3KG的足够了。我们需要控制摄像头左右、上下移动,所以需要2个舵机,一个转轴水平,一个转轴垂直。如果你只想左右移动,一个也可以凑合。
4,302胶水,¥2。
5,USB电缆一根,从坏掉的USB鼠标键盘弄来一根不要钱的。
6,带有LPT接口的电脑一台,大部分台式机都有,如果你是笔记本可以需要10块钱左右买一个USB To LPT的转接口了。
硬件连接
把你的两个舵机和摄像头固定起来,你可能会选择502胶水其实302效果更好虽然干得慢一些。首先把1号舵机立起来的使转轴垂直向上,然后在转轴上用胶水固定第2号舵机,2号舵机的转轴应该水平,最后把摄像头用胶水固定到2号舵机的转轴,最终效果就像这样。
你可能会觉得这个底座不错,其实是摄像头自带的,想要的话,你买这种摄像头就行了。40块钱,这个摄像头还带麦克风呢。
舵机控制线与电源线连接
1,你把两个舵机的地线(通常是黑色)连在一起合并成一根,然后弄一根线延长一些插入上图蓝色线的插口内。
2,把两个舵机的信号线(通常是白色)分别延长插入黄色和白色线的插口。
3,把你准备的USB线剪开,会发现四根线,其中红色连接到两个舵机的火线(通常也是红色),黑色连接到舵机的地线(通常也是黑色),确定连接没有问题,就把这个USB线插入电脑,这样舵机的电源供应就解决了。
4,固定LPT接口上不稳定的插线连接很必要,要不信号会不稳定,舵机会乱抖,这时你可以选择插入三个小钉子、牙签、或者像我一样使用剪断后的吉他D弦,用力插入后固定效果还是不错的,不过要注意不要搞短路了。
5,最后插入摄像头的USB线,这一步很简单。
最后我们看一下整体情况:
舵机地线(黑色)合并在一起,连接到LPT(第二排从左起1~8随便选),也同时连接到USB电源线的地线(黑色)。
舵机火线(红色)合并在一起,连接到USB电源线的火线。
舵机信号线(白色),分别单独连接到对应的LPT数据口。
插入USB电源线到主机,插入摄像头USB线到主机,连接完成。
准备编写控制软件
首先回顾一下我们的控制理论:
1,舵机信号线接收周期为20ms的电平信号,根据高电平的持续时间,决定舵机的转向角度。
2,LPT接口一次可以发送一个字节,一个字节=8位,所以LPT接口有8个数据端口,每个端口代表1位。如果这一位是0,该端口就是低电平0V。如果这一位是1,该端口就是高电平5V。其实LPT端口可以控制8个舵机,只是我们仅需要两个而已。
3,我们先给LPT端口发送0xff(1 1 1 1 1 1 1 1),然后1.5ms后发送0x00(0 0 0 0 0 0 0 0),如此往复。这样每个端口所连接的舵机就会接收到1.5ms的高电平信号,根据舵机角度与高电平时间的关系,所有连接的舵机都会指向90度的位置。
4,我们先给LPT端口发送0xff,然后1ms后发送0x01,在经过1ms后发送0x00,那么1号舵机接收到2ms高电平,2号舵机接收到1ms高电平,两个舵机就会指向不同的位置了。
5,最后我们得出发送字节与所控制的舵机的对应关系。
0x01:1通道舵机有信号。
0x02:2通道舵机有信号。
0x04:3通道舵机有信号。
0x08:4通道舵机有信号。
0x10:5通道舵机有信号。
0x20:6通道舵机有信号。
0x40:7通道舵机有信号。
0x80:8通道舵机有信号。
如果你想让1/2通道有信号,而其他通道没有信号,很简单发送0x01 OR 0x02 = 0x03就行了。
好了,控制理论我们已经很清楚了,接下来我们遇到了两个问题。
1,由于0.5ms~2.5ms这短短的2ms就代表了0~180度的角度变化,我们的定时器必须很精确才行,要不1ms的偏差就会导致90度的角度偏差,这是不能容忍的。
2,我们需要直接向IO端口写入字节,而Win32的保护机制是不允许你这么干的,你要是用DOS可能会容易点。
不过不要灰心,第一个问题可以使用WindowsAPI QueryPerformanceFrequency/QueryPerformanceCounter高精度计时器来解决,这个已CPU频率为基准的计时器的精确度足以满足我们的需要。简单来说QueryPerformanceFrequency返回你的硬件每秒钟震荡频率是多少次,在我的机器是2208064,函数QueryPerformanceCounter返回自开机以来你的硬件晶体已经震荡了多少次。具体可以查阅MSDN。
第二个问题有WinIO可以帮助我们,这是一个兼容所有Windows平台的让你可以进入RING0来直接读写IO或物理内存的免费函数库。WinIO官方网站(www.internals.com)
舵机控制部分源代码
unit ThreadServo; interface uses SysUtils, Windows, Classes, SyncObjs, WinIo; const MAX_CHANNELS=8; type EServoThread = class(Exception); TServoThread = class(TThread) private FBase:Int64; FCurrent:Int64; FInterval:Int64; FFrequency:Int64; FMaxPulse:Int64; FMinPulse:Int64; FLastSignal:Byte; FSleeped:Boolean; FAllowSleep:Boolean; FLock:TCriticalSection; FChannels:array [1..MAX_CHANNELS] of Integer; FChannelsTime:array [1..MAX_CHANNELS] of Int64; procedure SetChannel(Index: Integer; const Value: Integer); procedure SetSignal(CH1,CH2,CH3,CH4,CH5,CH6,CH7,CH8:Boolean); procedure SetInterval(const Value: Integer); procedure SetMaxPulse(const Value: Integer); procedure SetMinPulse(const Value: Integer); public constructor Create; destructor Destroy; override; procedure Execute; override; property Channels[Index:Integer]:Integer write SetChannel; property Interval:Integer write SetInterval; property MaxPulse:Integer write SetMaxPulse; property MinPulse:Integer write SetMinPulse; property AllowSleep:Boolean write FAllowSleep; end; implementation { TServoThread } constructor TServoThread.Create; begin if not InitializeWinIo then //初始化WinIO,一边可以直接访问硬件IO raise EServoThread.Create('Initialize error.'); FLock:=TCriticalSection.Create; QueryPerformanceFrequency(FFrequency); //取得硬件晶体震荡频率 SetInterval(20000); //设置信号周期 单位是微妙,1毫秒=1000微妙 所以信号周期为20ms SetMaxPulse(2500); //最长脉冲2.5ms SetMinPulse(500); //最短脉冲0.5ms FAllowSleep:=True; //允许低电平的时候Sleep,节省CPU占用 inherited Create(False); end; destructor TServoThread.Destroy; begin Terminate; ShutdownWinIo; FLock.Free; inherited; end; procedure TServoThread.Execute; begin Priority:=tpTimeCritical; //提升线程优先级 QueryPerformanceCounter(FBase); while not Terminated do begin QueryPerformanceCounter(FCurrent); if FCurrent-FBase>FInterval then begin //每周期进入一次,初始化一些东西 FBase:=FCurrent; FSleeped:=False; end;
//设置LPT每一个通道的电平,8个参数对应,8个通道,True为高电平,False为第电平。 SetSignal(FCurrent-FBase<FChannelsTime[1], //根据本周期以进行的时间算出应该高电平还是低电平。下同 FCurrent-FBase<FChannelsTime[2], FCurrent-FBase<FChannelsTime[3], FCurrent-FBase<FChannelsTime[4], FCurrent-FBase<FChannelsTime[5], FCurrent-FBase<FChannelsTime[6], FCurrent-FBase<FChannelsTime[7], FCurrent-FBase<FChannelsTime[8]); end; end; procedure TServoThread.SetChannel(Index: Integer; const Value: Integer); begin
//设置每个通道的位置1024个级别,0=0度 1024=180度 if (Index<1) or (Index>MAX_CHANNELS) then raise EServoThread.Create(Format('Index %d out of Channels.',[Index])); FLock.Enter; FChannels[Index]:=Value;
//根据通道位置级别算出对应的高电平脉冲时间,微秒。 FChannelsTime[Index]:=FMinPulse+Round((FMaxPulse-FMinPulse)*(Value/1024)); FLock.Leave; end; procedure TServoThread.SetInterval(const Value: Integer); begin FInterval:=Round(FFrequency*(Value/1000/1000)); end; procedure TServoThread.SetMaxPulse(const Value: Integer); begin FMaxPulse:=Round(FFrequency*(Value/1000/1000)); end; procedure TServoThread.SetMinPulse(const Value: Integer); begin FMinPulse:=Round(FFrequency*(Value/1000/1000)); end; procedure TServoThread.SetSignal(CH1,CH2,CH3,CH4,CH5,CH6,CH7,CH8:Boolean); var S:Integer; Signal:Byte; begin Signal:=0; if CH1 then Signal:=Signal or $01; if CH2 then Signal:=Signal or $02; if CH3 then Signal:=Signal or $04; if CH4 then Signal:=Signal or $08; if CH5 then Signal:=Signal or $10; if CH6 then Signal:=Signal or $20; if CH7 then Signal:=Signal or $40; if CH8 then Signal:=Signal or $80;
//因为IO操作很费时还可能导致时间片切换,所以不重复写入相同的IO值 if FLastSignal<>Signal then begin FLastSignal:=Signal;
//注意这个$378,这代表第一个LPT端口的数据位IO地址。第二个LPT数据位IO地址为$278 if not SetPortVal($378,Signal,1) then raise EServoThread.Create('SetPortVal Error.'); end;
//低电平时间允许通过Sleep把CPU还给操作系统,以免CPU占用100%的尴尬。 if (Signal=0) and FAllowSleep and not FSleeped then begin S:=Trunc((FInterval-(FCurrent-FBase))/FFrequency*1000); Sleep(S-1); FSleeped:=True; end; end; end.
总结
本章描述了从如何连接硬件、接线,以及通过WinIo直接控制IO输入,并通过高精度计时器生成规律的脉冲信号控制舵机。
由于时间关系还没写完的内容包括:
1,编写一个小型HTTP服务器,用WEB页面控制舵机,使得远程控制成为可能。
2,抓取摄像头视频并通过HTTP服务传送给客户端。
3,编写一些JS代码,把视频传送和舵机控制结合在一个页面。
4,制定一个密码机制,不能让所有人都可以访问你的摄像头。
5,动态IP不可知怎么不办?花生壳
6,内网还需要在路由做端口映射
不过其实软件已经全部完成,由于本章已经把硬件连接的方法给出,我先把编译后的完整程序发出来,有兴趣的朋友接上舵机就可以先用了。
软件下载地址:https://files.cnblogs.com/manors/ServoControlV03.rar
我还会在几天内更新第三部分,敬请关注。
更新1:运行软件后钩上Remote Port和Camera后面的Enable,然后再浏览器打入 http://localhost:81/ 输入admin密码fakepeter就可以看了,如果需要远程请设置防火墙或路由端口映射。
更新2:刚从手机里翻出来一段测试时候的控制视频,有兴趣可以看看