树莓派智能车AlphaBot教程11:Python 网络编程

来自丢石头百科


前面都是介绍通过web网页控制小车,这一章介绍通过App控制小车。


Python网络编程主要基于socket实现,socket在本质上与文件句柄、文件描述符、管道描述符等都是一个概念,都可以对其进行I/O处理。


什么是 Socket? Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。


好吧,说了你也不懂,直接上程序。新建一个serve.py文件,输入如下代码 python简单网络编程


服务端: 新建一个serve.py文件,输入如下代码

=== cpp代码: ===

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# file:server.py
 
import socket               
 
s = socket.socket()            # creat socket 
host = 192.168.8.1           # set ip
port = 2001                    # Set port
s.bind((host, port))           # Bind the port
 
s.listen(5)                    # Wait for the client to connect
while True:
    c, addr = s.accept()       # Create a client connection.
    print 'connect address:', addr
    c.send('Hello World!')
    c.close()                   # Close the connection


分析: 程序中首先使用socket模块的socket()函数来创建一个socket对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。通过调用 bind(hostname, port) 函数来指定服务的 port(端口)。 接着调用 socket 对象的 accept 方法。该方法等待客户端的连接,并返回 connection 对象,表示已连接到客户端。最后调用recv()接受客户端发送的字符,调用send()发送hello world给客户端并用close()关闭连接。


客户端:


=== cpp代码: ===

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# file:client.py
 
import socket               
 
s = socket.socket()         # creat socket 
host = '192.168.8.1'        # set ip
port = 2001                 # Set port
 
s.connect((host, port))     # connect serve
print s.recv(1024)          # recieve data
s.close()                   # Close the connection



分析: 程序中首先使创建一个socket对象。通过connect()函数连接到服务端。ip地址和port端口都需要和服务端设置一置。接着接收服务端发过来的数据并显示,最后断开连接。


现在打开两个终端,第一个终端先执行服务端server.py程序 sudo python server.py


第二个终端执行客户端clien.py程序 sudo python clien.py


此时第一个终端会显示如下信息。 connect address: ('192.168.10.235', 33788)


第二个终端会显示hello world Hello World!


python多线程网络编程


上面介绍 的只是简单的网络编程,通过循环阻塞的方式等等客户端链接。而且不能多个客户端同时连接。 下面介绍通过多线程非阻塞的方式实现网络编程。 服务端


=== cpp代码: ===

import SocketServer 
from SocketServer import StreamRequestHandler as SRH  
from time import ctime  
   
host = '192.168.8.1'  
port = 2001
addr = (host,port)  
   
class Servers(SRH):  
  def handle(self):  
    print 'got connection from ',self.client_address  
    self.wfile.write('connection %s:%s at %s succeed!' % (host,port,ctime()))  
    while True:  
      data = self.request.recv(1024)  
      if not data:   
        break  
      print data  
      self.request.send(data)  
             
print 'server is running....'  
server = SocketServer.ThreadingTCPServer(addr,Servers)  
server.serve_forever()


分析:


SocketServer是python网络编程的一个高级模块,ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个"线程",该线程用来和客户端进行交换。类中必须定义一个handle的方法 (因为父类即SocketServer.BaseRequestHandler中的handle方法为空)。程序最后启动ThreadingTCPServer。 在handle方法中,程序线打印客户端地址,发送连接程序信息给客服端。最后不断接受客户端发来的信息显示并发送会给客户端。


客户端


=== cpp代码: ===

from socket import *  
   
host = '192.168.8.1'  
port =  2001
bufsize = 1024  
addr = (host,port)  
client = socket(AF_INET,SOCK_STREAM)  
client.connect(addr)  
while True:  
    data = raw_input()  
    if not data or data=='exit':  
        break  
    client.send('%s\r\n' % data)  
    data = client.recv(bufsize)  
    if not data:  
        break  
    print data.strip()  
client.close()



分析:


客户端程序和上面的程序类似,创建一个socket对象,并连接服务端,程序中将键盘输入的信息发送到服务端, 接受服务端发过来的数据 并显示。


首先打开第一个终端先执行服务端程序 sudo python server.py


第二个终端执行客户端程序 sudo python clien.py


此时第一个终端会显示如下信息。 connect address: ('192.168.10.235', 33788)


第二个终端输入字符串并按确定键服务端会显示接受到的信息。另外多开几个终端运行客户端程并发送信息,可以看到服务端同样会接收到信息,换局域网内其他ip地址一样可以和服务端通讯。



通过软件控制小车。


要现实软件控制小车,小车必须作为服务端,接受客户端发送过来的命令,然后做出相应的动作。 废话不多说,直接上程序。


=== cpp代码: ===

import threading
import SocketServer 
import RPi.GPIO as GPIO
from SocketServer import StreamRequestHandler as SRH  
from AlphaBot import AlphaBot
from PCA9685 import PCA9685
from time import ctime  
 
 
Ab = AlphaBot()
pwm = PCA9685(0x40)
pwm.setPWMFreq(50)
 
BUZ = 4
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(BUZ,GPIO.OUT)
 
#Set the Horizontal servo parameters
HPulse = 1500  #Sets the initial Pulse
HStep = 0      #Sets the initial step length
pwm.setServoPulse(0,HPulse)
 
#Set the vertical servo parameters
VPulse = 1500  #Sets the initial Pulse
VStep = 0      #Sets the initial step length
pwm.setServoPulse(1,VPulse)
 
host = '192.168.10.235'  
port = 8000
addr = (host,port)  
 
def beep_on():
    GPIO.output(BUZ,GPIO.HIGH)
def beep_off():
    GPIO.output(BUZ,GPIO.LOW)
     
class Servers(SRH): 
    def handle(self): 
        global HStep,VStep 
        print 'got connection from ',self.client_address  
        self.wfile.write('connection %s:%s at %s succeed!' % (host,port,ctime()))  
        while True:  
            data = self.request.recv(1024)  
            if not data:   
                break  
            if data == "Stop":
                HStep = 0
                VStep = 0
                Ab.stop()
            elif data == "Forward":
                Ab.forward()
            elif data == "Backward":
                Ab.backward()
            elif data == "TurnLeft":
                Ab.left()
            elif data == "TurnRight":
                Ab.right()
            elif data == "Up":
                VStep = -5
            elif data == "Down":
                VStep = 5
            elif data == "Left":
                HStep = 5
            elif data == "Right":
                HStep = -5
            elif data == "BuzzerOn":
                beep_on()
            elif data == "BuzzerOff":
                beep_off()
            else:
                value = 0
                try:
                    value = int(data)
                    if(value >= 0 and value <= 100):
                        print(value)
                        Ab.setPWMA(value);
                        Ab.setPWMB(value);
                except:
                    print("Command error")
            print data   
            #print "recv from ", self.client_address[0]  
            self.request.send(data)  
             
def timerfunc():
    global HPulse,VPulse,HStep,VStep,pwm
     
    if(HStep != 0):
        HPulse += HStep
        if(HPulse >= 2500): 
            HPulse = 2500
        if(HPulse <= 500):
            HPulse = 500
        #set channel 2, the Horizontal servo
        pwm.setServoPulse(0,HPulse)    
         
    if(VStep != 0):
        VPulse += VStep
        if(VPulse >= 2500): 
            VPulse = 2500
        if(VPulse <= 500):
            VPulse = 500
        #set channel 3, the vertical servo
        pwm.setServoPulse(1,VPulse)   
     
    global t        #Notice: use global variable!
    t = threading.Timer(0.02, timerfunc)
    t.start()
     
t = threading.Timer(0.02, timerfunc)
t.setDaemon(True)
t.start()
 
print 'server is running....'  
server = SocketServer.ThreadingTCPServer(addr,Servers)  
server.serve_forever()



分析: ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个"线程"。handle()方法中接受客户端发过来的信息,并判断是已定义的命令,如果是则完成相应的动作。timerfunc()为定时器任务,控制舵机的。前面章节已经介绍过。


手机打开App可以连接小车控制。



电脑端打开AlphaBot.exe程序,输入IP地址,mjpg-stream的端口号,小车Soket服务端的端口号, 点击Viedeo Connet连接视频,按键变绿色即连接成功,点击Cmd Connect,连接小车服务端。如果按键变绿则连接成功。左边的按键控制小车移动,右边按键控制摄像头舵机转动。



114203n7e9znrg3tqqa0nr.png