前面都是介绍通过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,连接小车服务端。如果按键变绿则连接成功。左边的按键控制小车移动,右边按键控制摄像头舵机转动。 |