超市购物机器人

主要内容

机器人按照给定的购物列表,识别出物品的种类并将指定的物品放入到属于它的其对应的货架上。
robot

大致方案

比赛中机器人需要具有移动和物品分类的能力。上位机运行python利用mxnet进行迁移学习识别物品,然后用广度优先搜索规划机器人的路径传给STM32单片机控制机器人移动,机器人的移动是用红外模拟和数字传感器配合STM32单片机实现的,同时使用serial模块和二次通信协议控制机械臂取出和放置物品。

物品识别实现方法

普通的图像识别进行训练需要很大的样本,但是我们这次比赛12个物品每个物品大约只用摄像头拍摄了1000张,数据量远远不够,所以使用迁移学习,迁移学习是把一个源领域学习到的知识,迁移到另一个目标领域,是的目标能取得更好的学习效果。迁移学习的模型选用resnet50_v2,阿里2018FashionAI衣服分类比赛也用的这个模型进行迁移学习,效果较好。迁移学习将该模型的网络参数w和b全部复制过来,输出层重新定义并随即初始化,加载的模型有features和output两个成员变量,对应前面讲述的前者和后者。
该学习使用了Gluon,是的实现模型更加简洁:data模块提供了有关数据处理的工具、nn模块定义了大量神经网络的层,loss模块定义了各种损失函数、Trainer。
准备数据集、定义模型结构(前向传播)、定义损失函数和优化算法(反向传播)、训练模型
训练模型时,首先将图片增强,随机将亮度增加或减少在0%~50%之间,并将三个通道数值做标准化每个像素点转成浮点型并归一化,每个batch有(16,3,224,224)维度的数据,随后通过迁移学习模型的features层,每个batch得到(16, 2048)维度的数据。
将该数据通过两个全连接层,并过激活函数relu,再经过输出层得到每一个batch(16, 12)维度的数据,使用交叉熵作为损失,用梯度下降方法进行模型训练,打印loss值,准确率观察效果。
实验中使用cpu进行训练速度较慢,每次识别可能需要5~6秒,后改为GPU,大大提高了训练和识别的速度,且由于使用了迁移学习,最后的识别效果很好。

机器人移动部分

机器人的移动由STM32单片机控制,以格子为单位,每一个格子都是用白线分隔好的。机器人四周的部分数字传感器计算机器人走的格子数目,中间的两个红外模拟传感器收集到的数据通过PID参数调节控制机器人直线行驶,移动的指令由上位机根据物品应摆放的位置发送给STM32。

缺点

由于场地的光线或者场地平整度的偏差,数字红外传感器非0即1的特点可能会使得机器人实际走的格子数和期望走的格子数有偏差,所以计算格子数目也改为了模拟传感器。
前期机器人控制机器人行走,模拟红外数据的测量的调试都是通过串口,串口接上蓝牙,然后我在手机上自己做了一个蓝牙客户端扫描并连该蓝牙,进行一些数据的传递和控制。但是我没有想到的是,pid参数(控制机器人走直线和转弯,通过pwm波占空比的不同来控制四个轮子的转速)的调试我们都是代码改一次,烧录一次,浪费了很多的时间,而且当时比赛现场调试的时候时间紧迫,我要是当时想到直接手机app修改pid参数,然后通过蓝牙发送给STM32进行调试,速度会快得非常多。

机械臂的控制

我们买的机械臂可以进行二次开发,通过串口发送特定的数据,就能控制机械臂的一个动作,然后我们设计了抓取、放置的动作组,将该动作组数据存储在代码中,巧妙地完成了机械臂的问题。

我负责的部分

我主要负责物品的物品识别,之前看过李沐的机器学习视频,也写过数字手写识别的代码。在图片增强的时候我给图片增加了光线影响的问题,调试一些训练轮数、学习率batch_size等一些参数,使识别效果更好。然后我是该项目的队长,STM32控制机器人识别的部分我也参与的比较多,我写了一个iOS蓝牙应用程序,与插上蓝牙的单片机进行调试,比如调试时直接用手机控制机器人的运动,观察模拟红外的数据等等,唯一的遗憾是当时调试PID参数一直是用的keil修改代码、烧录代码花费很多时间,要是当时直接用手机应用通过蓝牙发送PID参数给单片机来调试会快很多。

附:机器学习训练及预测代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#preprocessing.py
#!/usr/bin/env python
# coding: utf-8

import mxnet as mx
from mxnet import autograd
from mxnet import gluon
from mxnet import image
from mxnet import init
from mxnet import nd
from mxnet.gluon.data import vision
from mxnet.gluon.model_zoo import vision as models
import numpy as np
from tqdm import tqdm #显示进度条
import matplotlib.pyplot as plt
#在notebook中显示matplotlib的图表
get_ipython().run_line_magic('matplotlib', 'inline')
#防止图片模糊
get_ipython().run_line_magic('config', "InlineBackend.figure_format = 'retina'")

ctx=mx.cpu()
train_augs=[
image.ForceResizeAug((224,224)),
image.BrightnessJitterAug(.5),
image.ColorNormalizeAug(mean=nd.array([0.485, 0.456, 0.406]), std=nd.array([0.229, 0.224, 0.225]))
]
val_augs=[
image.ForceResizeAug((224,224)),
image.ColorNormalizeAug(mean=nd.array([0.485, 0.456, 0.406]), std=nd.array([0.229, 0.224, 0.225]))
]
def transform(data, label,augs):
data = data.astype('float32') / 255
for pre in augs:
data = pre(data)

data = nd.transpose(data, (2,0,1))
return data, nd.array([label]).asscalar().astype('float32')

def get_features(net, data):
features = []
labels = []

for X, y in tqdm(data):
feature = net.features(X.as_in_context(ctx))
features.append(feature.asnumpy())
labels.append(y.asnumpy())

features = np.concatenate(features, axis=0)
labels = np.concatenate(labels, axis=0)
return features, labels

train_data = vision.ImageFolderDataset('/Users/JiaCheng/Downloads/match_data/E_Area_img/train', transform=lambda X, y: transform(X, y, train_augs))
val_data = vision.ImageFolderDataset('/Users/JiaCheng/Downloads/match_data/E_Area_img/val',transform=lambda X, y: transform(X, y, val_augs))
train_batches = gluon.data.DataLoader(train_data, 16)
val_batches = gluon.data.DataLoader(val_data, 16)

features_train, train_labels = get_features(models.resnet50_v2(pretrained=True, ctx=ctx), train_batches)
features_val, val_labels = get_features(models.resnet50_v2(pretrained=True, ctx=ctx), val_batches)

import h5py
with h5py.File('features_resnet_shopping.h5', 'w') as f:
f['train'] = features_train
f['train_label']=train_labels
f['val'] = features_val
f['val_label']=val_labels
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#top_train.py
#!/usr/bin/env python
# coding: utf-8

import mxnet as mx
from mxnet import autograd
from mxnet import gluon
from mxnet import image
from mxnet import init
from mxnet import nd
from mxnet.gluon.data import vision
from mxnet.gluon.model_zoo import vision as models
import numpy as np
from tqdm import tqdm #显示进度条
import matplotlib.pyplot as plt

ctx=mx.cpu()

def accuracy(output, labels):
return nd.mean(nd.argmax(output, axis=1) == labels).asscalar()

def evaluate(net, data_iter):
loss, acc, n = 0., 0., 0.
steps = len(data_iter)
for data, label in data_iter:
data, label = data.as_in_context(ctx), label.as_in_context(ctx)
output = net(data)
acc += accuracy(output, label)
loss += nd.mean(softmax_cross_entropy(output, label)).asscalar()
return loss/steps, acc/steps

import h5py
# with h5py.File('features_resnet_putting.h5', 'r') as f:
with h5py.File('features_resnet_shopping.h5', 'r') as f:
train_data = np.array(f['train'])
train_label = np.array(f['train_label'])
val_data = np.array(f['val'])
val_label = np.array(f['val_label'])

train_data.shape

train_label.shape

val_data.shape

val_label.shape

import mxnet as mx
from mxnet import gluon
from mxnet import ndarray as nd
from mxnet import autograd
from mxnet.gluon import nn
import h5py
import numpy as np

dataset_train = gluon.data.ArrayDataset(nd.array(train_data), nd.array(train_label))
dataset_val = gluon.data.ArrayDataset(nd.array(val_data), nd.array(val_label))

batch_size = 16
data_iter_train = gluon.data.DataLoader(dataset_train, batch_size, shuffle=True)
data_iter_val = gluon.data.DataLoader(dataset_val, batch_size)

net = nn.Sequential()
with net.name_scope():
net.add(nn.Dense(1024, activation='relu'))
net.add(nn.BatchNorm())
net.add(nn.Dense(512, activation='relu'))
net.add(nn.BatchNorm())
net.add(nn.Dense(12))

net.initialize(ctx=ctx)
#net.load_params('shopping_gpu1.params',ctx=ctx)
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': 1e-4, 'wd': 1e-5})

for epoch in range(30):
train_loss = 0.
train_acc = 0.
steps = len(data_iter_train)
for data, label in data_iter_train:
data, label = data.as_in_context(ctx), label.as_in_context(ctx)

with autograd.record():
output = net(data)
loss = softmax_cross_entropy(output, label)

loss.backward()
trainer.step(batch _size)
train_loss += nd.mean(loss).asscalar()
train_acc += accuracy(output, label)

val_loss, val_acc = evaluate(net, data_iter_val)
print("Epoch %d. loss: %.4f, acc: %.2f%%, val_loss %.4f, val_acc %.2f%%" % (
epoch+1, train_loss/steps, train_acc/steps*100, val_loss, val_acc*100))

net.save_parameters('/Users/JiaCheng/Downloads/match_data/shopping_cpu_04_05.params')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#predict.py
#!/usr/bin/env python
# coding: utf-8

import mxnet as mx
from mxnet import autograd
from mxnet import gluon
from mxnet import image
from mxnet import init
from mxnet import nd
from mxnet.gluon.data import vision
from mxnet.gluon.model_zoo import vision as models
import numpy as np
from tqdm import tqdm
from mxnet import image
import utils
import matplotlib.pyplot as plt

get_ipython().run_line_magic('matplotlib', 'inline')
get_ipython().run_line_magic('config', "InlineBackend.figure_format = 'retina'")

ctx=mx.gpu()

img_path='C:/Users/dell/Desktop/shopping/match_data/ABCD_Area_img/val/00/20180504_212914962537.jpg'
img=image.imdecode(open(img_path,'rb').read())

test_augs=[
image.ForceResizeAug((224,224)),
image.ColorNormalizeAug(mean=nd.array([0.485, 0.456, 0.406]), std=nd.array([0.229, 0.224, 0.225]))
]

img=img.astype('float32')
img=img/255
for aug in test_augs:
img=aug(img)
x = nd.transpose(img, (2,0,1))
x=x.expand_dims(0)

base_model=models.resnet50_v2(pretrained=True, ctx=ctx)
feature =base_model.features(x.as_in_context(ctx))

from mxnet.gluon import nn
net = nn.Sequential()
with net.name_scope():
net.add(nn.Dense(1024, activation='relu'))
net.add(nn.BatchNorm())
net.add(nn.Dense(512, activation='relu'))
net.add(nn.BatchNorm())
net.add(nn.Dense(2))
net.load_params('putting_gpu1.params',ctx=ctx)

y=net(feature.as_in_context(ctx))

preds=nd.softmax(y)

preds

#kind=['红色方砖','养乐多','羽毛球','黄色方砖','爽歪歪酸奶','钢丝球','蓝色方砖','雪花啤酒','苹果','绿色方砖','雪碧','网球','空']
kind=['没有物体','有物体']
pred = int(nd.argmax(preds, axis=1).asscalar())
prob = preds[0][pred].asscalar()
print('类别:',kind[pred],'可能性:%.2f%%'%(prob*100))
img=image.imdecode(open(img_path,'rb').read())
plt.imshow(img.asnumpy())
本人才疏学浅,若有任何不严谨或者错误的地方,欢迎指正:Scotteland@163.com

Author: Jcwang

Permalink: http://example.com/2020/05/06/%E8%B6%85%E5%B8%82%E8%B4%AD%E7%89%A9%E6%9C%BA%E5%99%A8%E4%BA%BA/