diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index b76600b..6100072 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -74,7 +74,6 @@ encoding//\u7A0B\u5E8F\u91CD\u542F/AutoRestart.py=utf-8 encoding//\u7A97\u53E3\u91CD\u542F/RestartMainWindow.py=utf-8 encoding//\u7B80\u5355\u7684\u7A97\u53E3\u8D34\u8FB9\u9690\u85CF/WeltHideWindow.py=utf-8 encoding//\u7F51\u7EDC\u64CD\u4F5C/TcpSocket/\u63A7\u5236\u5C0F\u8F66/ControlCar.py=utf-8 -encoding//\u7F51\u7EDC\u64CD\u4F5C/TcpSocket/\u63A7\u5236\u5C0F\u8F66/imageserver.py=utf-8 encoding//\u7F51\u7EDC\u64CD\u4F5C/TcpSocket/\u63A7\u5236\u5C0F\u8F66/server.py=utf-8 encoding//\u817E\u8BAF\u89C6\u9891\u70ED\u64AD\u5217\u8868/TencentMovieHotPlay.py=utf-8 encoding//\u817E\u8BAF\u89C6\u9891\u70ED\u64AD\u5217\u8868/TencentMovieHotPlay_Flow.py=utf-8 diff --git a/网络操作/TcpSocket/控制小车/ControlCar.py b/网络操作/TcpSocket/控制小车/ControlCar.py index 379fe5c..0a5757e 100644 --- a/网络操作/TcpSocket/控制小车/ControlCar.py +++ b/网络操作/TcpSocket/控制小车/ControlCar.py @@ -22,14 +22,12 @@ __Version__ = 1.0 class ControlCar(QWidget): - HOST = "127.0.0.1" - PORT_CAR = 8888 # 控制小车的端口 - PORT_IMAGE = 8899 # 获取图片的端口 + HOST = '127.0.0.1' + PORT = 8888 def __init__(self, *args, **kwargs): super(ControlCar, self).__init__(*args, **kwargs) - self._connCar = None # 控制小车的连接 - self._connImage = None # 获取图片的连接 + self._connCar = None # 加载UI文件 uic.loadUi('carui.ui', self) self.resize(800, 600) @@ -56,11 +54,6 @@ class ControlCar(QWidget): self._connCar.deleteLater() del self._connCar self._connCar = None - if self._connImage: - self._connImage.close() - self._connImage.deleteLater() - del self._connImage - self._connImage = None def closeEvent(self, event): """窗口关闭事件""" @@ -71,6 +64,7 @@ class ControlCar(QWidget): def doConnect(self): """连接服务器""" self.buttonConnect.setEnabled(False) + self._timer.stop() self._clearConn() self.browserResult.append('正在连接服务器') # 连接控制小车的服务器 @@ -79,18 +73,7 @@ class ControlCar(QWidget): self._connCar.disconnected.connect(self.onDisconnected) # 绑定连接丢失信号 self._connCar.readyRead.connect(self.onReadyRead) # 准备读取信号 self._connCar.error.connect(self.onError) # 连接错误信号 - self._connCar.connectToHost(self.HOST, self.PORT_CAR) - # 连接获取图片的服务器 - self._connImage = QTcpSocket(self) - # 200毫秒,无闪烁 - self._connImage.connected.connect( - lambda: self._timer.start(200) and self.browserResult.append('图片服务器连接成功')) - self._connImage.disconnected.connect( - lambda: self._timer.stop() and self.browserResult.append('图片服务器连丢失连接')) - self._connImage.error.connect(lambda _: self._timer.stop() and self.browserResult.append( - '连接图片服务器错误: ' + self._connImage.errorString())) - self._connImage.readyRead.connect(self.onImageReadyRead) - self._connImage.connectToHost(self.HOST, self.PORT_IMAGE) + self._connCar.connectToHost(self.HOST, self.PORT) def onConnected(self): """连接成功""" @@ -101,9 +84,12 @@ class ControlCar(QWidget): self.sliderLeft.setEnabled(True) self.sliderRight.setEnabled(True) self.browserResult.append('连接成功') # 记录日志 + # 开启获取摄像头图片定时器 + self._timer.start(200) def onDisconnected(self): """丢失连接""" + self._timer.stop() self.buttonConnect.setEnabled(True) # 按钮可用 # 设置初始拉动条不可用 self.sliderForward.setEnabled(False) @@ -122,23 +108,17 @@ class ControlCar(QWidget): while self._connCar.bytesAvailable() > 0: try: data = self._connCar.readAll().data() - self.browserResult.append('接收到数据: ' + data.decode()) + if data and data.find(b'JFIF') > -1: + self.qlabel.setPixmap( # 图片 + QPixmap.fromImage(QImage.fromData(data))) + else: + self.browserResult.append('接收到数据: ' + data.decode()) except Exception as e: self.browserResult.append('解析数据错误: ' + str(e)) - def onImageReadyRead(self): - """返回的图片数据""" - while self._connImage.bytesAvailable() > 0: - try: - data = self._connImage.readAll().data() - if data and data.find(b'JFIF') > -1: - self.qlabel.setPixmap( - QPixmap.fromImage(QImage.fromData(data))) - except Exception as e: - self.browserResult.append('解析图片数据错误: ' + str(e)) - def onError(self, _): """连接报错""" + self._timer.stop() self.buttonConnect.setEnabled(True) # 按钮可用 self.browserResult.append('连接服务器错误: ' + self._connCar.errorString()) @@ -164,18 +144,12 @@ class ControlCar(QWidget): def doGetImage(self): # 请求图片 - if not self._connImage or not self._connImage.isWritable(): - return self.browserResult.append('图片服务器未连接或不可写入数据') - self._connImage.write(b'getimage\n') + self.sendData('getimage', '') def sendData(self, ver, data): """发送数据""" if not self._connCar or not self._connCar.isWritable(): return self.browserResult.append('服务器未连接或不可写入数据') -# self._connCar.write(ver.encode() + str(data).encode()) - # time.sleep(0.05) - - # 我的服务器需要接收以\n结尾的数据 self._connCar.write(ver.encode() + str(data).encode() + b'\n') diff --git a/网络操作/TcpSocket/控制小车/README.md b/网络操作/TcpSocket/控制小车/README.md index b31ad46..50a2fd7 100644 --- a/网络操作/TcpSocket/控制小车/README.md +++ b/网络操作/TcpSocket/控制小车/README.md @@ -13,6 +13,7 @@ - 这里只用了UI文件做界面,并没有转换为python代码 - server.py只是做个本地echo服务器用来测试命令是否正常,依赖tornado库,可以通过pip install tornado来安装 - 另外需要做粘包处理,以(\n)作为粘包符 + - 由于wifi能力不行,发送图片要尽量小 说明: @@ -22,4 +23,4 @@ - QTcpSocket.error 连接报错触发该信号(连接超时、服务器断开等等) ## 截图 -![截图](ScreenShot/car.gif) \ No newline at end of file +![截图](ScreenShot/1.png) \ No newline at end of file diff --git a/网络操作/TcpSocket/控制小车/ScreenShot/1.png b/网络操作/TcpSocket/控制小车/ScreenShot/1.png new file mode 100644 index 0000000..31480ad Binary files /dev/null and b/网络操作/TcpSocket/控制小车/ScreenShot/1.png differ diff --git a/网络操作/TcpSocket/控制小车/ScreenShot/car.gif b/网络操作/TcpSocket/控制小车/ScreenShot/car.gif deleted file mode 100644 index 274535f..0000000 Binary files a/网络操作/TcpSocket/控制小车/ScreenShot/car.gif and /dev/null differ diff --git a/网络操作/TcpSocket/控制小车/imageserver.py b/网络操作/TcpSocket/控制小车/imageserver.py deleted file mode 100644 index ece7b43..0000000 --- a/网络操作/TcpSocket/控制小车/imageserver.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import logging - -import cv2 -from tornado import gen -from tornado.ioloop import IOLoop -from tornado.iostream import StreamClosedError -from tornado.options import options, define -from tornado.tcpserver import TCPServer - - -# Created on 2018年4月18日 -# author: Irony -# site: https://github.com/892768447 -# email: 892768447@qq.com -# file: 控制小车.server -# description: -__Author__ = """By: Irony -QQ: 892768447 -Email: 892768447@qq.com""" -__Copyright__ = 'Copyright (c) 2018 Irony' -__Version__ = 1.0 - - -define("port", default=8899, help="TCP port to listen on") -logger = logging.getLogger(__name__) - -SIZE = (640, 480) # 分辨率 -FPS = 24 -PARAM = [int(cv2.IMWRITE_JPEG_QUALITY), FPS] - - -class EchoServer(TCPServer): - - IMAGE = None - - def __init__(self, *args, **kwargs): - super(EchoServer, self).__init__(*args, **kwargs) - try: - self.cap = cv2.VideoCapture(0) - except Exception as e: - print(e) - - @gen.coroutine - def handle_stream(self, stream, address): - while True: - try: - data = yield stream.read_until(b"\n") -# logger.info("Received bytes: %s", data) - if not data.endswith(b"\n"): - data = data + b"\n" - if data == b'getimage\n' and self.cap and self.cap.isOpened(): - _, frame = self.cap.read() # 读取一帧图片 - if str(type(frame)).find('ndarray') > -1: - frame = cv2.resize(frame, SIZE) - ret, data = cv2.imencode('.jpg', frame, PARAM) - if ret: - yield stream.write(data.tostring()) # 发送图片 - else: - yield stream.write(b'\n') - except StreamClosedError: - logger.warning("Lost client at host %s", address[0]) - break - except Exception as e: - print(e) - - -if __name__ == "__main__": - options.parse_command_line() - server = EchoServer() - server.listen(options.port) - logger.info("Listening on TCP port %d", options.port) - IOLoop.current().start() diff --git a/网络操作/TcpSocket/控制小车/server.py b/网络操作/TcpSocket/控制小车/server.py index e698db3..1dfa1c5 100644 --- a/网络操作/TcpSocket/控制小车/server.py +++ b/网络操作/TcpSocket/控制小车/server.py @@ -1,21 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -# Created on 2018年4月18日 -# author: Irony -# site: https://github.com/892768447 -# email: 892768447@qq.com -# file: 控制小车.server -# description: - -__Author__ = """By: Irony -QQ: 892768447 -Email: 892768447@qq.com""" -__Copyright__ = 'Copyright (c) 2018 Irony' -__Version__ = 1.0 - +import atexit import logging +import cv2 from tornado import gen from tornado.ioloop import IOLoop from tornado.iostream import StreamClosedError @@ -23,20 +11,123 @@ from tornado.options import options, define from tornado.tcpserver import TCPServer +try: + import RPi.GPIO as GPIO # @UnusedImport @UnresolvedImport +except: + pass + + +# Created on 2018年4月18日 +# author: Irony +# site: https://github.com/892768447 +# email: 892768447@qq.com +# file: server +# description: +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = 'Copyright (c) 2018 Irony' +__Version__ = 1.0 + + define("port", default=8888, help="TCP port to listen on") logger = logging.getLogger(__name__) +# SIZE = (640, 480) # 分辨率 +# FPS = 24 +SIZE = (100, 80) # 分辨率 +FPS = 5 +PARAM = [int(cv2.IMWRITE_JPEG_QUALITY), FPS] + class EchoServer(TCPServer): + + IMAGE = None + + def __init__(self, cap, *args, **kwargs): + super(EchoServer, self).__init__(*args, **kwargs) + self.cap = cap + self.init() + + def init(self): + """初始化管脚""" + try: + GPIO.setmode(GPIO.BCM) + GPIO.setup(17, GPIO.OUT) + GPIO.setup(27, GPIO.OUT) + GPIO.setup(22, GPIO.OUT) + GPIO.setup(18, GPIO.OUT, initial=False) + self.duo = GPIO.PWM(18, 300) + self.p1 = GPIO.PWM(17, 10000) + self.p2 = GPIO.PWM(27, 10000) + en = GPIO.output(22, False) # @UnusedVariable + self.p1.start(0) + self.p2.start(0) + self.duo.start(0) + except Exception as e: + print(e) + + def forward(self, a): + try: + k = a / 100 + print("k=%lf" % k) + self.p1.ChangeDutyCycle(k) + self.p2.ChangeDutyCycle(0) + except: + pass + + def back(self, a): + try: + k = a / 100 + print("k1=%lf" % k) + self.p1.ChangeDutyCycle(0) + self.p2.ChangeDutyCycle(k) + except: + pass + + def lr(self, a): + try: + self.duo.ChangeDutyCycle(a) # 左满舵a=32,右满舵a=52,中值a=42,则a值取32至52 + except: + pass + + def stop(self): + try: + self.p1.ChangeDutyCycle(0) + self.p2.ChangeDutyCycle(0) + self.duo.ChangeDutyCycle(42) + except: + pass + @gen.coroutine def handle_stream(self, stream, address): while True: try: data = yield stream.read_until(b"\n") - logger.info("Received bytes: %s", data) +# logger.info("Received bytes: %s", data) if not data.endswith(b"\n"): data = data + b"\n" - yield stream.write(data) + if data == b'getimage\n' and self.cap and self.cap.isOpened(): + _, frame = self.cap.read() # 读取一帧图片 + if str(type(frame)).find('ndarray') > -1: + frame = cv2.resize(frame, SIZE) + ret, data = cv2.imencode('.jpg', frame, PARAM) + if ret: + yield stream.write(data.tostring()) # 发送图片 + else: + try: + ver, value = data.decode().split(':') + if ver == 'L': + self.lr(int(value)) + elif ver == 'R': + self.lr(int(value)) + elif ver == 'F': + self.forward(int(value)) + elif ver == 'B': + self.back(int(value)) + yield stream.write(data) + except: + yield stream.write(b'\n') except StreamClosedError: logger.warning("Lost client at host %s", address[0]) break @@ -44,9 +135,27 @@ class EchoServer(TCPServer): print(e) -if __name__ == "__main__": +def start(cap): + """启动服务器""" options.parse_command_line() - server = EchoServer() + server = EchoServer(cap) server.listen(options.port) logger.info("Listening on TCP port %d", options.port) IOLoop.current().start() + + +if __name__ == "__main__": + cap = None + try: + cap = cv2.VideoCapture(0) # 开启摄像头 + atexit.register(lambda: cap.release()) + atexit.register(lambda: GPIO.cleanup()) + start(cap) + except Exception as e: + print(e) + if cap: + cap.release() + try: + GPIO.cleanup() + except: + pass