Dự kiến thực hiện sẽ gồm các bước như sau: Tạo dữ liệu bằng cách đọc ảnh các tờ tiền từ camera. Thiết kế mạng NN với đầu vào là ảnh (224,224,3), đưa vào mạng MobileNET và đầu ra của MobileNET sẽ dùng để đưa vào 1 mạng NN nhỏ kết thúc bằng 1 lớp Dense và hàm softmax. Đầu ra sẽ là 1 vector softmax chứa các probality p(i) ứng với mỗi class i, chúng ta sẽ in ra giá trị max trong vector đó và chọn đó làm class dự đoán.
Vấn đề về dữ liệu Theo kế hoạch, làm sample với 2 loại tiền là 10000 và 20000. Tuy nhiên do tổng các p(i) phải = 1 nên giả sử khi không có đồng tiền nào thì máy sẽ buộc phải dự đoán vào 1 trong 2 loại tiền nói trên. Vì vậy chúng ta sẽ train với 3 class output là “00000”,”10000” và “20000”, output sẽ là Dense(3). Do máy không có dữ liệu tiền mệnh giá khác hai mệnh giá trên nên khi nhận diện tiền mệnh giá khác sẽ xảy ra hiện tượng nhận diện sai thành “10000” hoặc “20000”
60
nên ta phải tạo thêm dữ liệu tiền mệnh giá khác và đưa vào tập dữ liệu “00000” để huấn luyện cho máy.
3.2.2. Các bước thực hiện
3.2.2.1. Tạo cơ sở dữ liệu cho bài toán
Dữ liệu ảnh đầu vào cần có chất lượng ảnh cao và ít bị ảnh hưởng bởi nhiễu do các yếu tố môi trường bên ngoài như ánh sáng. Cài đặt phần mềm DroidCam trên máy tính và điện thoại di động có camera chất lượng cao. Sử dụng điện thoại như một webcam chất lượng cao (64MP) cho laptop và lấy được các ảnh chất lượng 720P hoặc 1080P
Hình 3.8 DroidCam
Cách tạo dữ liệu là viết một đoạn code Python đọc ảnh liên tục và lưu lại vào các thư mục đã tạo sẵn tương ứng ảnh các tờ tiền là : “00000”, “1000”, “2000”, “5000”, “10000”, “20000”. Đưa dữ liệu mệnh giá “1000”, “2000”, “5000” vào thư mục mệnh giá “00000”.
61
Hình 3.9 Dữ liệu "00000"
(a) dữ liệu “00000” trên một ảnh, (b) tập dữ liệu “00000”
(a) (b)
Hình 3.10 Dữ liệu "10000"
(a) dữ liệu “10000” trên một ảnh, (b) tập dữ liệu “10000”
(a) (b)
Hình 3.11 Dữ liệu "20000"
(a) dữ liệu “20000” trên một ảnh, (b) tập dữ liệu “20000” 3.2.2.2. Train model
Tải dữ liệu lên Google Drive, thực hiện train model trên Google Colab. Gán nhãn cho dữ liệu train như hình dưới
62
Hình 3.12 Dữ liệu đã gán nhãn
Sử dụng thư viện “train test split” để chia dữ liệu thành 2 phần gồm 70% dữ liệu dùng để train và 30% dữ liệu còn lại dùng cho validation.
Sử dụng hàm “ImageDataGenerator” để làm phong phú tập dữ liệu, tránh hiện tượng overfitting bằng các phép xoay hình, thu phóng và dịch chuyển hình ảnh, …
63
Hình 3.13 Data augumentation
Thiết kế mạng CNN Classify để train model, ta sử dụng mạng MobileNET có đầu vào là ảnh kích thước (224,224,3), và đầu ra của MobileNET sẽ dùng để đưa vào 1 mạng NN nhỏ kết thúc bằng 1 lớp Dense và hàm Softmax
Hàm Sotfmax dịch ra Tiếng Việt là hàm trung bình mũ. Nói một cách khái quát, hàm softmax sẽ tính khả năng xuất hiện của một class trong tổng số tất cả các class có thể xuất hiện. Sau đó, xác suất này sẽ được sử dụng để xác định class mục tiêu cho các input. Cụ thể, hàm softmax biến vector k chiều có các giá trị thực bất kỳ thành vector k chiều có giá trị thực lớn hơn 0 và nhỏ hơn 1 và có tổng các giá trị bằng 1. Giá trị nhập có thể dương, âm, bằng 0 hoặc lớn hơn 1, nhưng hàm softmax sẽ luôn biến chúng thành một giá trị nằm trong khoảng [0:1].
64
Tiếp theo chúng ta sẽ tải dữ liệu ảnh đã tạo trước đó lên Google Drive và liên kết với Google Colab. Để tránh hiện tượng model bị Overfit vì dữ liệu nhiều nhưng đa phần giống nhau. Dẫn đến train sẽ có chất lượng tốt nhưng khi test sẽ thấy không chính xác cho lắm, chúng ta sẽ sử dụng hàm “ImageDataGenerator” để làm phong phú cho dữ liệu đầu vào (zoom to nhỏ, quay, kéo dãn, tăng giảm độ sáng,… cho ảnh).
Thực hiện train model với các siêu tham số:
num_epochs = 100 - Số lượng lần huấn luyện đi qua hết 1 vòng tập dữ liệu learning_rate = 0.00016 - Tỷ lệ học của thuật toán
batch_size = 64 - Số lượng mẫu dữ liệu trong một batᴄh
Sau khi train model, ta được kết quả train model với độ chính xác ~ 99%
Hình 3.15 Độ chính xác của model trên tập dữ liệu Val
Chuyển đổi file weight nhận được sau khi train model về file ONNX - Open Neural Network Exchange, là một công cụ đóng vai trò như một trung gian hỗ trợ chuyển đổi mô hình học máy từ các framework khác nhau về dạng ONNX cung cấp nhờ đó giúp chúng ta chuyển đổi dễ dàng giữa các framework khác nhau. ONNX hỗ trợ chuyển đổi giữa nhiều framework phổ biến hiện nay như Keras, Tensorfow, Scikit-learn, Pytorch và XGBoost – và tải về máy.
65
3.2.2.3. Test model
Sau khi tải file weight về, chúng ta load file, chạy thử model và hiệu chỉnh
66
3.3. Tính toán thiết kế tích hợp hệ thống điện, hệ thống điều khiển vào máy bán hàng
Hệ thống thiết bị điện sử dụng bao gồm:
1. Vi xử lý Raspberry Pi 3B( Thông số đã giới thiệu trong báo cáo)
Hình 3.17 Vi xử lý Raspberry Pi 3B
2. Vi xử lý Arduino Uno R3 (Thông số đã giới thiệu trong báo cáo)
Hình 3.18 Vi xử lý Arduino Uno R3
3. Nút nhấn
Hình 3.19 Nút nhấn
- Loại nút: Nút nhấn nhả. - Điện áp: 250VAC/3A.
67 - Số chân: 2 - Màu sắc: Xanh. - Kích Thước: 17x21mm. 4. Động cơ DC 12V – 2A Hình 3.20 Động cơ DC 12V-2A - Điện áp : 12VDC.
- Dòng không tải : 120mA. - Tốc độ không tải : 22 RPM.
- Momen xoắn không tải : 13.5 kg.cm. - Dòng tải 400mA 5. Module L298 Hình 3.21 Module L298 - IC chính: L298. - Vcc: DC 5V ~ 35V. - Dòng max: 2A.
68 - Kích thước: 55mmx49mmx33mm. - Trọng lượng: 33g.
- Dòng không tải:10mA.
- Chân tín hiệu điều khiển: 4 chân INT1, INT2, INT3, INT4. - Chân ra điều kiển động cơ: 4 chân OUT1, OUT2, OUT3, OUT4. - Chân điều kiển mạch cầu H: 2 chân ENA, ENB.
6. Nguồn 12V- 2A
Hình 3.22 Nguồn 12V-2A
- Điện áp ngõ vào:100~240VAC, 50/60Hz. - Điện áp ngõ ra: 12VDC.
- Dòng điện ngõ ra tối đa: 2A (nếu sử dụng liên tục nên cung cấp ở mức 80% công suất).
- Kiểu giắc ngõ ra: Chuẩn Jack DC tròn 5.5*2.1~2.5mm. - Chiều dài dây dẫn: 1m.
7. PC Camera 720
69 - Loại cảm biến: CMOS.
- Độ phân giải cao nhất: 1280 * 720. - Tốc độ khung hình: 30FPS.
- Giao diện: USB 2.0. - Chất liệu: ABS.
- Chiều dài cáp: 1,5 mét.
- Kích thước: 59,5mm x 63mm. 8. Module LCD 16x02 I2C
Mạch điều khiển màn hình 16x02 giao tiếp I2C sử dụng IC điều khiển màn hình kí tự gồm 16 cột và 2 dòng giúp tiết kiệm dây nối với vi điều khiển cho khả năng hiển thị nhanh với nhiều chức năng.
Hình 3.24 Module LCD 16x02 I2C
Thông số kỹ thuật
- Địa chỉ mặc định: 0x27, có thể mắc vào I2C bus tối đa 8 module (3bit address set).
- Điện áp hoạt động: 3V-6V.
- Để điều khiển độ tương phản của màn hình, xoay biến trở màu xanh. Với mạch điều khiển, nhóm sự dụng giao tiếp UART giữa Raspberry Pi với Arduino để điều khiển hệ thống. Hệ thống được mô tả như sau:
1. Cảm biến quang nhận diện tiền được nhét vào khe truyền dữ liệu về cho vi xử lý.
70
3. Nếu không đúng 10000 VND hoặc 20000 VND thì động cơ 1 quay ngược lại trả lại tiền; nếu tờ tiền là 10000 VND hoặc 20000 VND thì động cơ 2 cuộn tiền xuông khay chứa tiếp tục thực hiện bước 4.
4. Nếu là 10000 VND thì được mua vật phẩm 1 lần; nếu là 20000 VND thì được mua vật phẩm 2 lần.
5. Chọn vật phẩm cần mua thông qua nút nhấn tương ứng trên bảng điều khiển.
6. Khi đó, động cơ tương ứng tại vị trí đó sẽ được bật, thông qua cơ cấu chuyển động biến chuyển động quay thành chuyển động tịnh tiến khiến vật rơi xuống khoang lấy đồ.
72
Chương 4: KẾT LUẬN VÀ ĐỊNH HƯỚNG PHÁT TRIỂN
4.1. Kết quả đạt được
73
- Nghiên cứu, thiết kế thành công hệ thống nhận diện tiền Việt Nam ứng dụng DeepLearning trên Raspberry Pi.
- Tính toán, thiết kế và mô phỏng hệ thống khung cơ khí 3D của mô hình trên phần mềm Solidworks.
- Tìm hiểu, thiết kế được hệ thống điều khiển của mô hình và mô phỏng trên phần mềm Proteus.
- Tìm hiểu và ứng dụng các linh kiện điện tử vào thiêt kế hệ thống điều khiển như L298, relay, cảm biến quang,…
- Phát huy kỹ năng làm việc nhóm, tăng hiệu quả công việc.
4.2. Hạn chế
Sản phẩm chạy ổn định, cho kết quả tốt, tuy nhiên vẫn còn một số hạn chế sau:
- Do thời gian hạn chế và thiếu kiến thức, kinh nghiệm thực tế nên độ chính xác của mô hình nhận diện tiền giấy chưa cao, mô hình thiếu tính thẩm mỹ.
- Mô hình thiết kế chưa có tính tự động hóa. - Mô hình chưa có phần trả lại tiền thừa.
- Chưa có hệ thống giá đỡ sản phẩm sau khi rơi
Bảng 4.1 Kết quả thử nghiệm của loại tiền 10000 VND
Lần thử Loại tiền Kết quả Kết luận
1 10000 0 sai 2 10000 20000 sai 3 10000 10000 đúng 4 10000 10000 đúng 5 10000 10000 đúng 6 10000 10000 đúng 7 10000 10000 đúng 8 10000 10000 đúng 9 10000 10000 đúng 10 10000 10000 đúng 11 10000 20000 sai 12 10000 20000 sai 13 10000 10000 đúng 14 10000 10000 đúng
74 15 10000 20000 sai 16 10000 20000 sai 17 10000 10000 đúng 18 10000 10000 đúng 19 10000 10000 đúng 20 10000 10000 đúng Tỷ lệ đúng 70%
Bảng 4.2 Kết quả thử nghiệm của loại tiền 20000 VND
Lần thử Loại tiền Kết quả Kết luận
1 20000 20000 đúng 2 20000 20000 đúng 3 20000 20000 đúng 4 20000 20000 đúng 5 20000 20000 đúng 6 20000 20000 đúng 7 20000 20000 đúng 8 20000 20000 đúng 9 20000 20000 đúng 10 20000 20000 đúng 11 20000 20000 đúng 12 20000 20000 đúng 13 20000 20000 đúng 14 20000 20000 đúng 15 20000 20000 đúng 16 20000 20000 đúng 17 20000 0 sai 18 20000 20000 đúng 19 20000 20000 đúng 20 20000 20000 đúng Tỷ lệ đúng 95%
75
4.3. Định hướng phát triển
- Ứng dụng DeepLearning vào giải quyết nhiều bài toán thực tế khác như phát hiện lái xe ngủ gật, phát hiện đeo khẩu trang, đọc biển số xe, … - Phát triển các thuật toán, phần mềm, chương trình điều khiển sử dụng
AI-DeepLearning vào các ứng dụng thực tế, các máy móc và robot như hệ thống phát hiện và tránh vật cản trên robot hút bụi, trợ lý ảo,…
76
DANH MỤC VỀ TÀI LIỆU THAM KHẢO
Tài liệu tham khảo
1. Vũ Trung Kiên; Phạm Văn Chiến; Nguyễn Văn Tùng, Giáo trình vi điều khiển PIC, Khoa học và Kỹ thuật, 2014.
2. Trịnh Chất; Lê Văn Uyển, Tính toán thiết kế hệ dẫn động cơ khí, NXB Giáo Dục, 2006.
3. Blum; Jeremy, Exploring Arduino: Tools and Techniques For Engineering Wizardry, NXB John Wiley&Sons, 2020.
4. Nguyễn Thanh Tuấn, Deep learning cơ bản V2, 2020.
5. Ian Goodfellow, Yoshua Bengio, Aaron Courville, Deep learning adaptive computation and machine learning series, 2015
6. Berton Earnshaw, Savvysherpa, A brief survey of tensors, 2017
Website tham khảo
[1] What are Convolutional Neural Networks? | IBM
[2]Thuật toán CNN - Convolutional Neural Network | TopDev
[3]Tìm hiểu mạng MobileNetV1 - Phạm Duy Tùng Machine Learning Blog (phamduytung.com)
[4]python.org
77
PHỤ LỤC
Code trên Raspberry Pi 3
Tạo cơ sở dữ liệu
import numpy as np import cv2
import time import os
# Label: 00000 là ko cầm tiền, còn lại là các mệnh giá label = "00000"
cap = cv2.VideoCapture(2)
# Biến đếm, để chỉ lưu dữ liệu sau khoảng 60 frame, tránh lúc đầu chưa kịp cầm tiền lên
i=00
while(True):
# Capture frame-by-frame
# i+=1
ret, frame = cap.read() if not ret:
continue
frame = cv2.resize(frame, dsize=None,fx=1,fy=1)
# Hiển thị
cv2.imshow('frame',frame)
# Lưu dữ liệu
if i>=60:
print("Số ảnh capture = ",i-60)
# Tạo thư mục nếu chưa có
if not os.path.exists('data/' + str(label)): os.mkdir('data/' + str(label))
cv2.imwrite('data/' + str(label) + "/" + str(i) + ".png",f rame)
if cv2.waitKey(1) & 0xFF == ord('q'): break
# When everything done, release the capture cap.release()
78
Train model
# Khai báo thư viện Tensorflow import tensorflow as tf
print(tf.__version__)
# Khai báo các thư viện cần thiết khác
from tensorflow.keras.preprocessing.image import ImageDataGenerato
r
from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from imutils import paths from tqdm import tqdm import matplotlib.pyplot as plt import numpy as np import random import shutil import glob import time import cv2 import os
from zipfile import ZipFile from google.colab import drive
drive.mount('/content/drive')
num_epochs = 100 # Số lượng lần huấn luyện đi qua hết 1 vòng tập d
ữ liệu
learning_rate = 0.00016 # Tỷ lệ học của thuật toán
batch_size = 64
# Xóa dữ liệu cũ
!rm -rf /content/data
!rm -rf /content/data_raw
# Tải và giải nén dữ liệu
!mkdir /content/data_raw %cd /content/drive/MyDrive/Money_Classify/Train13 zf = ZipFile('/content/drive/MyDrive/Money_Classify/Train13/data.z ip', 'r') zf.extractall('/content/data_raw') zf.close() %cd /content/data_raw
all_img_paths = list(paths.list_images("."))
79 # Trộn dữ liệu và hiện các hình ảnh np.random.seed(0) random.shuffle(all_img_paths) print(all_img_paths[:5]) # Visualize images plt.figure(figsize=(15,15)) for i in range(25): image_path = np.random.choice(all_img_paths) image = plt.imread(image_path) image = cv2.resize(image, (224, 224))
# you might want to verify the labels before
# you put this to use
label = image_path.split("/")[1] plt.subplot(5, 5, i+1) plt.xticks([]) plt.yticks([]) plt.grid(True) plt.imshow(image) plt.xlabel(label) plt.show() !rm -rf /content/data/ val_ratio = 0.30 train_dir = "/content/data/train" val_dir = "/content/data/val"
val_imgs = int(len(all_img_paths) * val_ratio)
train_imgs = len(all_img_paths) - val_imgs
train_img_paths = all_img_paths[:train_imgs] val_img_paths = all_img_paths[train_imgs:] # Function for copying images into subset folder
def copy_images(img_paths, output_dir): for imagePath in img_paths:
# extract the label from the current image path
label = imagePath.split("/")[1]
# check if a directory for the label exists, if not, create it
imageDir = os.path.join(output_dir, label)
if not os.path.exists(imageDir):
os.makedirs(imageDir)
# copy the current image to the respective folder
shutil.copy2(imagePath, imageDir) copy_images(train_img_paths, train_dir) copy_images(val_img_paths, val_dir) # Switch to /content/data
%cd /content/data
# Setup data generators
train_aug = ImageDataGenerator(rescale=1/255.,
rotation_range=20,
80
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=True,
brightness_range=[0.2, 1.5], fill_mode="nearest")
val_aug = ImageDataGenerator(rescale=1/255.)
print("Training set")
train_gen = train_aug.flow_from_directory(train_dir, class_mode="categorical", target_size=(224, 224), color_mode="rgb", shuffle=True, batch_size=batch_size )
print("Validation set")
val_gen = train_aug.flow_from_directory(val_dir, class_mode="categorical", target_size=(224, 224), color_mode="rgb", shuffle=False, batch_size=batch_size )
num_classes = len(train_gen.class_indices.keys())
print("Set number of classes to {}".format(num_classes)) # Get the class labels and export to labels.txt
print(train_gen.class_indices)
labels = '\n'.join(sorted(train_gen.class_indices.keys()))
with open('labels.txt', 'w') as f: f.write(labels)
# Save number of training and validation samples to use later num_train_samples = len(list(paths.list_images("train"))) num_val_samples = len(list(paths.list_images("val")))
print("Train: {} images, Val: {} images".format(num_train_samples, num_val_samples))
# Switch to /content
%cd /content
def build_model():
base_model = tf.keras.applications.MobileNetV2(input_shape=(22
4, 224, 3), include_top=False, alpha=0.35)
base_model.trainable = False
classifier = Sequential() classifier.add(base_model)
classifier.add(GlobalAveragePooling2D())
classifier.add(Dropout(0.5))
81
classifier.add(Dropout(0.5))
classifier.add(Dense(128, activation='relu'))
classifier.add(Dropout(0.5))
classifier.add(Dense(64, activation='relu'))
classifier.add(Dropout(0.5))
classifier.add(Dense(num_classes, activation='softmax'))
classifier.compile(loss="categorical_crossentropy",
optimizer=tf.keras.optimizers.Adam(learn ing_rate), metrics=["accuracy"]) return classifier # Build model classification_model = build_model() # Setup a callback to save the best model best_model_path = "best_model_checkpoint.h5" model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint( filepath=best_model_path, save_weights_only=True, monitor='val_accuracy', mode='max', save_best_only=True) start = time.time() history = classification_model.fit_generator(train_gen, steps_per_epoch=num_train_samples // batch_size, validation_data=val_gen, validation_steps=num_val_samples // batch_size, epochs=num_epochs, callbacks=[model_checkpoint_callback] )
print("Total training time: ", time.time()-start)
# Load the best model weights
classification_model.load_weights(best_model_path)
classification_model.save(("/content/drive/MyDrive/Money_Classify/
Train16/MobileNetModel_train16.h5"))
# Plot training graph
N = len(history.history["loss"]) plt.figure()
plt.plot(np.arange(0, N), history.history["loss"], label="train_lo ss")
plt.plot(np.arange(0, N), history.history["val_loss"], label="val_ loss")
plt.plot(np.arange(0, N), history.history["accuracy"], label="trai n_acc")
82
plt.plot(np.arange(0, N), history.history["val_accuracy"], label=" val_acc")
plt.title("Training Loss and Accuracy") plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy") plt.legend(loc="lower left") plt.show()
eval_result = classification_model.evaluate(val_gen)
val_accuracy = eval_result[1]
print("Model accuracy on validation set: {}".format(val_accuracy))
!pip install -U tf2onnx
import onnx import tf2onnx
onnx_model, _ = tf2onnx.convert.from_keras(classification_model, o pset=11, output_path="/content/drive/MyDrive/Money_Classify/Train1 6/Money_classifier_Train16.onnx")
output_names = [n.name for n in onnx_model.graph.output]
print("Output names: ", output_names)
Link bài train trên Google Colab:
https://colab.research.google.com/drive/1nFGCpD5aXkTanSQU1dp9ONnD yu3f8x5q?usp=sharing
Test
import cv2
from utils import classify_image import serial
ser = serial.Serial(9600,'/dev/ttyUSB0')
# Init model model = cv2.dnn.readNetFromONNX( "Money_classifier_Train22.onnx") vid = cv2.VideoCapture(2) while(True):
83 if (ser.in_waiting > 0): d = ser.read() d = d.decode() d1 = d.rstrip() print(d1) if (d1 == 1):
result = classify_image(frame, model)
cv2.putText(frame, result, (50, 50),