目录
一、原理图
由于树莓派3b性能有限,所以只能通过电脑进行预测。
二、硬件介绍及成果展示
树莓派3b+
麦克纳姆轮
usb摄像头:640*480
视频展示:
三、pc端操作及关键源码
3.1接收树莓派发来的图片
import socket
import os
import sys
import struct
import result
import scanimage
def socket_service():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# IP地址留空默认是本机IP地址
s.bind(('', 8088))
s.listen(7)
except socket.error as msg:
print(msg)
sys.exit(1)
print("连接开启,等待传图...")
sock, addr = s.accept()
deal_data(sock, addr)
s.close()
def deal_data(sock, addr):
print("成功连接上 {0}".format(addr))
while True:
fileinfo_size = struct.calcsize('128sl')
buf = sock.recv(fileinfo_size)
if buf:
filename, filesize = struct.unpack('128sl', buf)
fn = filename.decode().strip('\x00')
# PC端图片保存路径
new_filename = os.path.join('D:\AI_project\gluoncv', fn)
recvd_size = 0
fp = open(new_filename, 'wb')
while not recvd_size == filesize:
if filesize - recvd_size > 1024:
data = sock.recv(1024)
recvd_size += len(data)
else:
data = sock.recv(1024)
recvd_size = filesize
fp.write(data)
fp.close()
sock.close()
class_id, scores, bounding_boxs = scanimage.scan(r'D:\AI_project\gluoncv\image.png')
# # print('class_id:', class_id)
# # print('scores:', scores)
# # print('bounding_boxs:', bounding_boxs)
os.remove(r'D:\AI_project\gluoncv\image.png')
file = open(r"D:\AI_project\gluoncv\result.txt", 'w')
file.write(str(class_id))
file.write('\n')
file.write(str(scores))
file.write('\n')
file.write(str(bounding_boxs))
break
if __name__ == '__main__':
socket_service()
将树莓派和PC保持在同一个网络中。使用socket包互相传递信息。
注:手机热点获取树莓派ip地址方法:
(49条消息) 手机热点ip查看(用于远程树莓派)_宇智波洛必达的博客-CSDN博客
3.2处理接收到的图像
调用gluoncv
from gluoncv import model_zoo, data, utils
下载yolo3_darknet53_coco模型,并把树莓派上传的截图放进去进行预测。
预测结果是
class_ID:预测到的目标标签
scores:得分(应该是叫可信度还是啥的?)
bounding_boxs:[预测框左上角坐标,右下角坐标]
net = model_zoo.get_model('yolo3_darknet53_coco', pretrained=True, root='D:\AI_project\gluoncv\model')
x, img = data.transforms.presets.yolo.load_test(dir, short=512)
class_IDs, scores, bounding_boxs = net(x)
# print('Shape of pre-processed image:', x.shape)
# print('class:', class_IDs[0][0])
# print('scores:', scores[0][0])
# print('bounding_box:', bounding_boxs[0][0])
另外,bounding_boxs的坐标系如下图:
3.3将结果(txt)发回给树莓派
import socket
import os
import sys
import struct
def send_txt():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 192.168.199.1和8088分别为服务端(pc)的IP地址和网络端口
s.connect(('192.168.150.59', 8077))
except socket.error as msg:
print(msg)
print(sys.exit(1))
while True:
# filepath是要被发送图片的路径
filepath = r'D:\AI_project\gluoncv\result.txt'
fhead = struct.pack(b'128sl', bytes(os.path.basename(filepath), encoding='utf-8'), os.stat(filepath).st_size)
s.send(fhead)
print('client filepath: {0}'.format(filepath))
fp = open(filepath, 'rb')
while 1:
data = fp.read(1024)
if not data:
print('{0} 发送成功...'.format(filepath))
break
s.send(data)
s.close()
break
发完后,这一轮的操作就和电脑再没关系了,电脑回到等待接收图像的状态,以此循环。
四、树莓派端操作及关键代码
4.1 截图,并上传到电脑
上传部分和电脑给raspberry发txt的过程完全一直,不再赘述。
4.2 接受电脑返回的txt
和电脑等待接受截图时完全一致。
4.3 读取txt
# @Time : 2022/9/11 11:15
# @Author :lgl
# @e-mail :GuanlinLi_BIT@163.com
def read_result(file_route):
global y1, x2, y2, x1
file = open(file_route, 'r')
line1 = file.readline()
label = file.readline()
line3 = file.readline()
line4 = file.readline()
score = file.readline()
line5 = file.readline()
line6 = file.readline()
coordinate = file.readline()
label = (label.split("[")[1]).split(".")[0]
score = (score.split("[")[1]).split("]")[0]
coordinate = coordinate.split(" ")
num = 0
for i in coordinate:
if len(i) != 1 and len(i) != 0 and num == 0:
x1 = i
num += 1
continue
if len(i) != 1 and len(i) != 0 and num == 1:
y1 = i
num += 1
continue
if len(i) != 1 and len(i) != 0 and num == 2:
x2 = i
num += 1
continue
if len(i) != 1 and len(i) != 0 and num == 3:
y2 = i
num += 1
continue
x1 = x1.split("[")
x1 = x1[len(x1)-1]
y2 = y2.split("]")[0]
return label, score, x1, y1, x2, y2
read_result("/home/pi/Desktop/gluoncv/result.txt")
print(x1,y1,x2,y2)
4.4 根据参数做出小车的运动决策
from motor import *
from random import random
def move(s, x1, y1, x2, y2, t):
s = float(s)
x1 = float(x1)
y1 = float(y1)
x2 = float(x2)
y2 = float(y2)
t = float(t)
if s < 0.5: # 得分过低,找不到目标,需要随机移动,直到找到东西
forward(t)
x = random()
if x > 0.7:
left_move(t)
if x < 0.3:
right_move(t)
if 0.6 > x > 0.4:
back(t)
else:
if x2 - x1 < 300: # 找到了目标,但是距离太远,需要接近目标
forward(t)
print("forward")
else:
if x1 > 640 or x2 > 640 or x1 < 0 or x2 < 0 or y1 > 480 or y2 > 480 or y1 < 0 or y2 < 0:
# 图像显示不全,证明距离太近了,需要后退
back(t / 2)
print("back")
else:
if (x1 + x2) / 2 > 400: # 目标偏左
right_move(t)
print("right_move")
if (x1 + x2) / 2 < 240: # 目标偏右
left_move(t)
print("left_move")
if 240 <= (x1 + x2) / 2 <= 400: # 位置合适,不动,接着拍摄即可
print("OK!!!")
五、项目源码
树莓派小车实现目标追踪(yolo3,coco数据集,gluoncv,树莓派和PC信息交互)-Python文档类资源-CSDN文库