树莓派笔记14:舵机云台(二) 远程控制云台

前面一篇已经准备好云台控制模块了,接着想实现的效果就是可以远程控制云台动作,比如在笔记本电脑上通过按方向键控制云台进行水平和垂直方向上的转动。

1 分析

首先考虑用哪种方式与树莓派通信,这里我选择用UDP的方式向树莓派发送控制指令,主要原因是传输过程简单,不需要在通信逻辑上费精力。采用UDP协议的socket,电脑端不停地发控制指令,树莓派则循环接收指令,不需要去建立连接,反正只是近距离测试,基本不会在传输上有问题。

接着考虑控制逻辑,我想实现的效果是按住某个方向键,云台就持续向某个方位转动,松开方向键就停止。为此可以这样设计,在电脑端,设定一个状态量,按下方向键的时候设置对应的位为1,松开的时候设置对应的位为0,同时socket不断地把这个状态发出去;在树莓派上,循环接收指令并读取状态,根据状态量的情况控制云台进行对应的动作,如果状态值都为0,则停止。

最后要考虑的是接收端的阻塞问题,因为树莓派端需要不停地调用socket的接收函数接收指令,同时对云台进行控制,而默认的recvfrom函数是阻塞的,为了不让程序“卡”住,可以使用setblocking()方法设置socket,参数为0就把socket设置为非阻塞模式,这样调用recvfrom如果缓冲区没数据,则抛出socket.error异常,我们只要忽略这个异常就可以继续往下运行了。

2 电脑端程序

下面显示电脑端的程序,电脑端用python处理键盘事件太麻烦,所以使用C#写了个简单的winform应用,首先初始化一个字节数组作为状态标志,第一个字节0xff是个头标志,后面四个字节分别指示上下左右:
树莓派笔记14:舵机云台(二) 远程控制云台_第1张图片
接着为KeyDown和KeyUp分别进行事件处理:
树莓派笔记14:舵机云台(二) 远程控制云台_第2张图片
树莓派笔记14:舵机云台(二) 远程控制云台_第3张图片
为了发送数据,初始化socket,并设置一个定时器,在定时处理中把这个状态量发出去:
树莓派笔记14:舵机云台(二) 远程控制云台_第4张图片
这里写图片描述

3 树莓派程序

下面是树莓派上的python程序,这里要把前一篇文章中写好的Steering模块导进来:

import socket
from Steering_Module.Steering import Steering
'''
舵机控制程序
'''

HOST='192.168.191.2' 
PORT=9999
buffSize=1024
closeNow=False

state=['\xff','\x00','\x00','\x00','\x00'] #控制状态变量
# 初始化socket
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind((HOST,PORT))
server.setblocking(0) #设置为非阻塞模式
# 初始化舵机模块
steer=Steering(14,0,180,15,90,180,36,160) 
steer.setup()

print('Start!')
while closeNow is False:
    try:
        data,address=server.recvfrom(buffSize) #非阻塞模式,recvfrom立即返回,没有数据抛出socket.error异常
        if (len(data)==5 and data[0]=='\xff' and data[1]=='\xff' and  #如果是停止信号则结束程序
            data[2]=='\xff' and data[3]=='\xff' and data[4]=='\xff'):
            print("Exit!")
            closeNow=True
        elif (len(data)==5 and data[0]=='\xff'): #根据传来的控制信号更新状态变量
            state[1]=data[1]
            state[2]=data[2]
            state[3]=data[3]
            state[4]=data[4]
    except socket.error:
        pass

    #根据状态变量的值进行相应的动作
    if(state[1]=='\x01'):
        steer.Up()
        continue
    elif(state[2]=='\x01'):
        steer.Down()
        continue
    elif(state[3]=='\x01'):
        steer.Left()
        continue
    elif(state[4]=='\x01'):
        steer.Right()
        continue
    else:
        pass

代码里为了正常停止程序,当接收到五个字节都是0xff时跳出循环。这里有个地方困惑了我好久,就是判断state[]里的值时,我一开始是直接用state[i]==0x01 这种写法,但是这是不对的,得写成state[i]=='\x01' ,可以用type()方法查看0x01和’\x01’的类型,一个int,一个是str,而python默认的接收格式是以str类型存储,类型不一致当然就不能比较state[i]和0x01了。

4 效果

你可能感兴趣的:(树莓派笔记)