树莓派智能车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,连接小车服务端。如果按键变绿则连接成功。左边的按键控制小车移动,右边按键控制摄像头舵机转动。