Dans ce tutoriel, nous allons entrainer un modèle MobileNetV2 TensorFlow avec Keras pour qu’il s’applique à notre problématique. Nous allons ensuite pouvoir l’utiliser ne temps réel pour classifier de nouvelles images.
Pour ce tutoriel, nous supposons que vous avez suivit les tutoriel précédent: utilisation d’un modèle TensorFlow et préparation d’une base de données pour l’entrainement.
N.B.: je n’ai pas trouvé la bonne méthode pour entrainer le model ssd mobilenetV2, tel quel, avec tensorflow. Je suis donc passé sous Yolo. Si vous avez la bonne méthode n’hésitez pas à laisser un commentaire.
Pour entrainer le modèle, vous pouvez utiliser le script suivant qui va:
charger et augmenter la base de données
créer un modèle (model) à partir du modèle MobileNetV2(base_model)
entrainer les nouveaux gains du model
entrainer finement les gains du base_model
import matplotlib.pyplot as plt
import numpy as np
import os
import tensorflow as tf
#_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
#path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
#PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')
PATH="./data/cats_and_dogs_filtered"
train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')
BATCH_SIZE = 32
IMG_SIZE = (160, 160)
#create train and validation sets
train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir,
shuffle=True,
batch_size=BATCH_SIZE,
image_size=IMG_SIZE)
validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir,
shuffle=True,
batch_size=BATCH_SIZE,
image_size=IMG_SIZE)
class_names = train_dataset.class_names
plt.figure(figsize=(10, 10))
for images, labels in train_dataset.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)
print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset))
print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))
#configure performance
AUTOTUNE = tf.data.AUTOTUNE
train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)
validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE)
test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)
#augmented data (usefull for small data sets)
data_augmentation = tf.keras.Sequential([
tf.keras.layers.RandomFlip('horizontal'),
tf.keras.layers.RandomRotation(0.2),
])
for image, _ in train_dataset.take(1):
plt.figure(figsize=(10, 10))
first_image = image[0]
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
augmented_image = data_augmentation(tf.expand_dims(first_image, 0))
plt.imshow(augmented_image[0] / 255)
plt.axis('off')
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
rescale = tf.keras.layers.Rescaling(1./127.5, offset=-1)
# Create the base model from the pre-trained model MobileNet V2
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
include_top=False,
weights='imagenet')
#or load your own
#base_model= tf.saved_model.load("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/saved_model")
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
print(feature_batch.shape)
base_model.trainable = False
base_model.summary()
#classification header
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)
prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)
#create new neural network based on MobileNet
inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
initial_epochs = 10
loss0, accuracy0 = model.evaluate(validation_dataset)
print("initial loss: {:.2f}".format(loss0))
print("initial accuracy: {:.2f}".format(accuracy0))
history = model.fit(train_dataset,
epochs=initial_epochs,
validation_data=validation_dataset)
#plot learning curves
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.ylim([min(plt.ylim()),1])
plt.title('Training and Validation Accuracy')
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
#fine tuning
base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))
# Fine-tune from this layer onwards
fine_tune_at = 100
# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
layer.trainable = False
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
optimizer = tf.keras.optimizers.RMSprop(learning_rate=base_learning_rate/10),
metrics=['accuracy'])
model.summary()
fine_tune_epochs = 10
total_epochs = initial_epochs + fine_tune_epochs
history_fine = model.fit(train_dataset,
epochs=total_epochs,
initial_epoch=history.epoch[-1],
validation_data=validation_dataset)
#plot fine learning curves
acc += history_fine.history['accuracy']
val_acc += history_fine.history['val_accuracy']
loss += history_fine.history['loss']
val_loss += history_fine.history['val_loss']
plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0.8, 1])
plt.plot([initial_epochs-1,initial_epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([initial_epochs-1,initial_epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
#evaluate
loss, accuracy = model.evaluate(test_dataset)
print('Test accuracy :', accuracy)
model.save('saved_models/my_model')
Utilisation du modèle entrainé
Vous pouvez utiliser le modèle entrainé pour classifier de nouvelles images contenant un seul type d’objet par image. Pour cela, il vous suffit de charger le modèle précédemment sauvegardé (saved_models/my_model)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ObjectRecognitionTFVideo.py
# Description:
# Use ModelNetV2-SSD model to detect objects on video
#
# www.aranacorp.com
# import packages
import sys
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import cv2
import tensorflow as tf
from PIL import Image
# load model from path
#model= tf.saved_model.load("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/saved_model")
model= tf.saved_model.load("./saved_models/my_model")
#model.summary()
print("model loaded")
#load class names
#category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS,use_display_name=True)
def read_label_map(label_map_path):
item_id = None
item_name = None
items = {}
with open(label_map_path, "r") as file:
for line in file:
line.replace(" ", "")
if line == "item{":
pass
elif line == "}":
pass
elif "id" in line:
item_id = int(line.split(":", 1)[1].strip())
elif "display_name" in line: #elif "name" in line:
item_name = line.split(":", 1)[1].replace("'", "").strip()
if item_id is not None and item_name is not None:
#items[item_name] = item_id
items[item_id] = item_name
item_id = None
item_name = None
return items
#class_names=read_label_map("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/mscoco_label_map.pbtxt")
class_names = read_label_map("./saved_models/label_map.pbtxt")
class_names = list(class_names.values()) #convert to list
class_colors = np.random.uniform(0, 255, size=(len(class_names), 3))
print(class_names)
if __name__ == '__main__':
# Open image
#img= cv2.imread('./data/cats_and_dogs_filtered/train/cats/cat.1.jpg') #from image file
img= cv2.imread('./data/cats_and_dogs_filtered/train/dogs/dog.1.jpg') #from image file
img = cv2.resize(img,(160,160))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#input_tensor = np.expand_dims(img, 0)
input_tensor = tf.convert_to_tensor(np.expand_dims(img, 0), dtype=tf.float32)
# predict from model
resp = model(input_tensor)
print("resp: ",resp)
score= tf.nn.sigmoid(resp).numpy()[0][0]*100
cls = int(score>0.5)
print("classId: ",int(cls))
print("score: ",score)
print("score: ",tf.nn.sigmoid(tf.nn.sigmoid(resp)))
# write classname for bounding box
cls=int(cls) #convert tensor to index
label = "{}".format(class_names[cls])
img = cv2.resize(img,(640,640))
cv2.putText(img, label, (5, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, class_colors[cls], 2)
# Show frame
cv2.imshow("Frame", img)
cv2.waitKey(0)
Applications
reconnaissance de différentes races d’animaux
reconnaissance de différents type d’objets comme des cartes électroniques
Nous allons voir dans ce tutoriel comment mettre en place l’entrainement d’un modèle YOLO pour de la reconnaissance d’objets sur des données spécifiques. La difficulté se trouve dans l’élaboration de la banque d’images qui servira pour l’entrainement
Matériel
Un ordinateur avec une installation de Python3
Une caméra
Principe
Nous avons vu dans un précédent tutoriel comment reconnaitre des objets avec Yolo. Ce modèle a été entrainé pour détecter un certain nombre d’objets mais cette liste est limitée.
Une fois que vous avez créer une base de données d’images avec label et boites au format Yolo Placer la base de données dans le dossier YOLO\datasets. Ensuite, vous pouvez résumer les informations dans un fichier YAML dans lequel vous spécifiez:
le chemin de la base de données contenue dans datasets (coffe_mug)
N.B.: vous pouvez passer les chemin d’accès comme répertoire d’images, fichier texte (path/images.txt), ou list de chemin ([path1/images/, path2/images/])
Il est possible de récupérer un modèle pré-entrainé à partir du script python qui servira de base pour l’entrainement du nouveau modèle.
# load the pre-trained YOLOv8n model
model = YOLO("yolov8n.pt")
N.B.: regardé bien le modèle qui correspond à votre machine et à votre besoin car ils ont des performances différentes.
Script Python pour l’entrainement de Yolo
Une fois votre banque d’image prête, le script d’entrainement est assez simple. Il suffit de spécifier:
le nom du nouveau modèle (yolov8n_v8_50e)
le nombre d’itération (epochs)
la base de données à utiliser (data)
le nombre de fichier à utiliser sur une itération (batch)
train_yolo.py
from ultralytics import YOLO
# Load the model.
model = YOLO('yolov8n.pt')
# Training.
results = model.train(
data='coffee_mug_v8.yaml',
imgsz=1280,
epochs=50,
batch=8,
name='yolov8n_v8_50e'
)
L’algorithme d’entrainement enregistre un certains nombre de données pendant le process que vous pouvez visualiser à la suite pour analyser l’entrainement. Les résultats se trouve dans le dossier .\runs\detect\
Script Python pour l’évaluation du modèle
Une fois le modèle entrainé, vous pouvez comparer ses performances sur de nouvelles images.
Pour récupérer le modèle entrainé, vous pouvez le copier à la racine ou entrer le chemin d’accès
« ./runs/detect/yolov8n_v8_50e2/weights/best.pt »
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import datetime
from ultralytics import YOLO
import cv2
from imutils.video import VideoStream
#from helper import create_video_writer
# define some constants
CONFIDENCE_THRESHOLD = 0.65
GREEN = (0, 255, 0)
image_list=['./datasets/coffee_mug/test/10.png','./datasets/coffee_mug/test/19.png']
# load the pre-trained YOLOv8n model
#model = YOLO("yolov8n.pt")
model = YOLO("./runs/detect/yolov8n_v8_50e2/weights/best.pt") # test trained model
for i,img in enumerate(image_list):
#detect on image
frame= cv2.imread(img)#from image file
detections = model(frame)[0]
# loop over the detections
#for data in detections.boxes.data.tolist():
for box in detections.boxes:
#extract the label name
label=model.names.get(box.cls.item())
# extract the confidence (i.e., probability) associated with the detection
data=box.data.tolist()[0]
confidence = data[4]
# filter out weak detections by ensuring the
# confidence is greater than the minimum confidence
if float(confidence) < CONFIDENCE_THRESHOLD:
continue
# if the confidence is greater than the minimum confidence,
# draw the bounding box on the frame
xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)
#draw confidence and label
y = ymin - 15 if ymin - 15 > 15 else ymin + 15
cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)
# show the frame to our screen
cv2.imshow("Img{}".format(i), frame)
while True:
if cv2.waitKey(1) == ord("q"):
break
Résultats
L’objectif est atteint car nous obtenons un nouveau modèle qui reconnait les mugs ({0: ‘mug’}), seulement.
Vous pouvez tester ce code avec votre webcam ou avec des photos, par exemple, pour voir les performances du modèle et de la reconnaissance d’objet
Pour permettre au modèle de reconnaitre plus de type d’objets, il faut rajouter des images de l’objet considéré dans la base de données.
Nous allons voir dans ce tutoriel comment faire de la reconnaissance d’objet avec Yolo et OpenCV en utilisant un réseau de neurones pré-entrainé grâce au deep learning.
Nous avons vu dans un précédent tutoriel comment reconnaitre des formes simples avec la vision par ordinateur. Cette méthode ne fonctionne que pour certaines formes simples prédéfinies. Si vous souhaitez reconnaitre une plus grande variété d’objets, le plus simple est d’utiliser l’intelligence artificielle.
Matériel
Un ordinateur avec une installation de Python3
Une caméra
Principe
L’intelligence artificielle est un domaine de l’informatique dans lequel le programme apprend par lui-même à effectuer certaines tâches. Notamment de la reconnaissance visuelle. Dans ce tutoriel, nous allons utiliser un réseau de neurones entrainé pour reconnaitre des formes particulières.
Il faut de nombreuse données pour pouvoir entrainer correctement un réseau de neurone. Il a été démontré que l’apprentissage était plus rapide sur un réseau de neurones entrainé pour autre chose. Par exemple, un réseau de neurones entrainé pour reconnaitre les chiens s’entrainera plus facilement à reconnaitre les chats.
Ultralytics propose les modèles YOLO qui sont simples d’utilisation et assez efficace pour la détection, la classification et le suivit
Pour chaque version, il existe différents modèle du plus léger et moins performant au plus performant mais plus lourd
YOLOv5n
YOLOv5s
YOLOv5m
YOLOv5l
YOLOv5x
YOLOv8n
YOLOv8s
YOLOv8m
YOLOv8l
YOLOv8x
(nano, small, medium, large and extra large)
Configuration de Python
Si ce n’est pas le cas, vous pouvez télécharger et installer Python 3
Vous pouvez ensuite installer les librairies nécessaires imutils, OpenCV, ultralytics
Il est possible de récupérer un modèle pré-entrainé à partir du script pyhton
# load the pre-trained YOLOv8n model
model = YOLO("yolov8n.pt")
Script Python pour la reconnaissance d’Objet
Tout d’abord, nous créons un flux vidéo (vs) à l’aide de la librairie imutils qui va récupérer les images de la caméra.
vs = VideoStream(src=0, resolution=(1600, 1200)).start()
Nous initialisons un réseau de neurones avec les paramètres du modèle à l’aide de la librairie YOLO (yolov5, v8, etc.).
model = YOLO("yolov8n.pt")
Nous allons, ensuite, créer une boucle qui à chaque itération va lire l’image de la caméra et la passer en entrée du réseau de neurone pour faire la détection et reconnaissance d’objet.
while True:
# start time to compute the fps
start = datetime.datetime.now()
#ret, frame = video_cap.read()
#frame = vs.read(); ret=True
# if there are no more frames to process, break out of the loop
if not ret:
break
# run the YOLO model on the frame
detections = model(frame)[0]
Enfin, le code affiche sur l’image la boite de détection, la probabilité de reconnaissance ainsi que la position
# loop over the detections
for box in detections.boxes:
#extract the label name
label=model.names.get(box.cls.item())
# extract the confidence (i.e., probability) associated with the detection
data=box.data.tolist()[0]
confidence = data[4]
# filter out weak detections
if float(confidence) < CONFIDENCE_THRESHOLD:
continue
# if the confidence is greater than the minimum confidence
xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)
#draw confidence and label
y = ymin - 15 if ymin - 15 > 15 else ymin + 15
cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)
Code complet de reconnaissance d’objet avec OpenCV et YOLO
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import datetime
from ultralytics import YOLO
import cv2
from imutils.video import VideoStream
#from helper import create_video_writer
# define some constants
CONFIDENCE_THRESHOLD = 0.8
GREEN = (0, 255, 0)
# load the pre-trained YOLOv8n model
model = YOLO("yolov8n.pt")
#model = YOLO("yolov5n.pt")
"""
#detect on image
frame= cv2.imread('./datasets/two-boats.jpg');ret= True #from image file
detections = model(frame)[0]
# loop over the detections
#for data in detections.boxes.data.tolist():
for box in detections.boxes:
#extract the label name
label=model.names.get(box.cls.item())
# extract the confidence (i.e., probability) associated with the detection
data=box.data.tolist()[0]
confidence = data[4]
# filter out weak detections by ensuring the
# confidence is greater than the minimum confidence
if float(confidence) < CONFIDENCE_THRESHOLD:
continue
# if the confidence is greater than the minimum confidence,
# draw the bounding box on the frame
xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)
#draw confidence and label
y = ymin - 15 if ymin - 15 > 15 else ymin + 15
cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)
# show the frame to our screen
cv2.imshow("Img", frame)
"""
#detect on video
# initialize the video capture object
#vs = VideoStream(src=0, resolution=(1600, 1200)).start()
video_cap = cv2.VideoCapture("datasets\\Splash - 23011.mp4")
# initialize the video writer object
#writer = create_video_writer(video_cap, "output.mp4")
while True:
# start time to compute the fps
start = datetime.datetime.now()
#ret, frame = video_cap.read()
#frame = vs.read(); ret=True
# if there are no more frames to process, break out of the loop
if not ret:
break
# run the YOLO model on the frame
detections = model(frame)[0]
# loop over the detections
#for data in detections.boxes.data.tolist():
for box in detections.boxes:
#extract the label name
label=model.names.get(box.cls.item())
# extract the confidence (i.e., probability) associated with the detection
data=box.data.tolist()[0]
confidence = data[4]
# filter out weak detections by ensuring the
# confidence is greater than the minimum confidence
if float(confidence) < CONFIDENCE_THRESHOLD:
continue
# if the confidence is greater than the minimum confidence,
# draw the bounding box on the frame
xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)
#draw confidence and label
y = ymin - 15 if ymin - 15 > 15 else ymin + 15
cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)
#cv2.circle(frame, (int(X)-15, int(Y)), 1, GREEN, 2)
#cv2.putText(frame, poslbl, (int(X), int(Y)),cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)
# end time to compute the fps
end = datetime.datetime.now()
# show the time it took to process 1 frame
total = (end - start).total_seconds()
print(f"Time to process 1 frame: {total * 1000:.0f} milliseconds")
# calculate the frame per second and draw it on the frame
fps = f"FPS: {1 / total:.2f}"
cv2.putText(frame, fps, (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 4)
# show the frame to our screen
cv2.imshow("Frame", frame)
#writer.write(frame)
if cv2.waitKey(1) == ord("q"):
break
video_cap.release()
vs.stop()
#writer.release()
cv2.destroyAllWindows()
Sources d’image pour la détection d’objet
Vous pouvez utiliser ce script avec différentes sources d’image. Pour cela, il faut légèrement adapter le code précédent afin de modifier la variable « img » contenant l’image à analyser.
La webcam de votre ordinateur
vs = VideoStream(src=0, resolution=(1600, 1200)).start()
while True:
frame = vs.read()
Le stream vidéo doit être arrêté à la fin du script avec vs.stop()
vc = cv2.VideoCapture('rtsp://user:password@ipaddress:rtspPort')
while True:
ret, frame=vc.read() #from ip cam
Veillez à arrêter la capture vidéo à la fin du script avec vc.release()
La Picam du Raspberry Pi
vs = VideoStream(usePiCamera=True, resolution=(1600, 1200)).start()
while True:
frame = vs.read()
Pensez à arrêter le stream à la fin du script avec vs.stop()
Un fichier vidéo
vc = cv2.VideoCapture('./datasets/Splash - 23011.mp4') #from video
while True:
ret, frame=vc.read() #from video
Un fichier image
frame= cv2.imread('./datasets/two-boats.jpg')
Résultats
Pour cet exemple nous envoyons en entrée du réseau de neurones une image de deux bateaux qui sont reconnus correctement. Pour obtenir des résultats légèrement différents, vous pouvez modifier le paramètre confidence pour éviter les faux positifs.
Vous pouvez tester ce code avec votre webcam ou avec des photos, par exemple, pour voir les performances du modèle et de la reconnaissance d’objet.
Une fois que votre script fonctionne, vous pouvez entrainer votre modèle pour qu’il puisse détecter d’autres objets.
Pour préparer une banque d’image en vue de l’entrainement d’un réseau de neurones à la reconnaissance d’objet, il faut reconnaitre soit même les images de la base de données. C’est à dire leur donner un label et une zone de reconnaissance.
Suivez les consignes de construction sur github. Vous pouvez aussi trouver un exécutable labelImg.exe
Ajouter une boite et un label
Lancer labelImg et sélectionner le dossier à l’aide du bouton « Open Dir ».
Pour chaque image, vous allez entourer l’objet à détecter et lui affecter un nom (label) à l’aide du bouton « Create RectBox ».
N.B.: évitez de dépasser de l’image lorsque vous dessinez la boîte. Cela peut poser des soucis lors de l’entrainement
Convertir au format PascalVOC
Convertir au format YOLO
N.B.: Vous pouvez sauvegarder les deux formats à la suite ou sauvegarder en VOC et convertir en YOLO à l’ai du script convert_voc_to_yolo.py
Préparer une banque d’image avec une architecture de dossier
L’idée est de placer les images dans des sous-dossiers du nom de la classe. Pour l’entrainement, la banque d’image doit contenir entre 1 et 3 dossiers: train, test, validation (les dossiers test et validation sont optionnels car ils peuvent être créés à partir du premier
N.B.: pour cette méthode il faut un seul objet par image
images
train
cats
dogs
validation
cats
dogs
Pour créer les fichiers contenant les info de nom et de zone de détection à partir des dossier images, vous pouvez utiliser le scripts generate_voc_files.py en modifiant:
les chemins d’accès vers les différents dossiers (folders[‘train’/’validation’/’test’])
Les noms de classe seront définies par les noms de dossier et la zone de détection par la taille des images.
generate_voc_files.py
import glob
import os
import pickle
import cv2
import xml.etree.ElementTree as ET
import xml.dom.minidom
from os import listdir, getcwd
from os.path import join
dirs = ['train', 'test']
classes = ['mug']
def getImagesInDir(dir_path):
image_list = []
for filename in glob.glob(dir_path + '/**/*.png', recursive=True):
image_list.append(filename)
return image_list
def generate_voc(image_path):
#get image data
dirname=os.path.dirname(image_path)
foldername=dirname.split('\\')[-1]
basename = os.path.basename(image_path)
basename_no_ext = os.path.splitext(basename)[0]
im = cv2.imread(image_path)
h,w,c=im.shape
root = ET.Element('annotation')
folder = ET.SubElement(root, 'folder')
folder.text=foldername
filename = ET.SubElement(root, 'filename')
filename.text=basename
path = ET.SubElement(root, 'path')
path.text=image_path
source = ET.SubElement(root, 'source')
database = ET.SubElement(source, 'database')
database.text = 'Unknown'
size = ET.SubElement(root, 'size')
width = ET.SubElement(size, 'width')
width.text='{}'.format(w)
height = ET.SubElement(size, 'height')
height.text='{}'.format(h)
depth = ET.SubElement(size, 'depth')
depth.text='{}'.format(c)
segmented = ET.SubElement(root, 'segmented')
segmented.text = '0'
objec = ET.SubElement(root, 'object')
name = ET.SubElement(objec, 'name')
name.text=foldername
pose = ET.SubElement(objec, 'pose')
pose.text='Unspecified'
truncated = ET.SubElement(objec, 'truncated')
truncated.text='0'
difficult = ET.SubElement(objec, 'difficult')
difficult.text='0'
bndbox = ET.SubElement(objec, 'bndbox')
xmin = ET.SubElement(bndbox, 'xmin')
xmin.text='{}'.format(0+5)
ymin = ET.SubElement(bndbox, 'ymin')
ymin.text='{}'.format(0+5)
xmax = ET.SubElement(bndbox, 'xmax')
xmax.text='{}'.format(w-5)
ymax = ET.SubElement(bndbox, 'ymax')
ymax.text='{}'.format(h-5)
tree = ET.ElementTree(root)
outxml=join(dirname,basename_no_ext+'.xml')
tree.write(outxml)
return outxml
def convert(size, box):
dw = 1./(size[0])
dh = 1./(size[1])
x = (box[0] + box[1])/2.0 - 1
y = (box[2] + box[3])/2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
def convert_annotation(in_file):
dirname=os.path.dirname(in_file)
basename = os.path.basename(in_file)
basename_no_ext = os.path.splitext(basename)[0]
out_file = open(join(dirname, basename_no_ext + '.txt'), 'w')
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult)==1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
cwd = getcwd()
for dir_path in dirs:
full_dir_path = join(cwd,dir_path)
image_paths = getImagesInDir(full_dir_path)
for image_path in image_paths:
xml_path=generate_voc(image_path) #generate voc file
convert_annotation(xml_path) #convert to yolo file
print("Finished processing: " + dir_path)
Cette méthode permet d’avoir rapidement une base de données exploitable pour l’entrainement (TensorFlow et Yolo) même si la zone de reconnaissance est approximative.
N.B.: Une fois les fichiers XML et TXT créés, vous pouvez ouvrir lableImg pour affiner les labels ainsi que la zone de détection.
Nous allons voir dans ce tutoriel comment faire de la reconnaissance d’objet avec TensorFlow et OpenCV en utilisant un réseau de neurones pré-entrainé grâce au deep learning.
Nous avons vu dans un précédent tutoriel comment reconnaitre des formes simples avec la vision par ordinateur. Cette méthode ne fonctionne que pour certaines formes simples prédéfinies. Si vous souhaitez reconnaitre une plus grande variété d’objets, le plus simple est d’utiliser l’intelligence artificielle.
Matériel
Un ordinateur avec une installation de Python3
Une caméra
Principe
L’intelligence artificielle est un domaine de l’informatique dans lequel le programme apprend par lui-même à effectuer certaines tâches. Notamment de la reconnaissance visuelle. Dans ce tutoriel, nous allons utiliser un réseau de neurones entrainé pour reconnaitre des formes particulières.
Il faut de nombreuse données pour pouvoir entrainer correctement un réseau de neurone. Il a été démontré que l’apprentissage était plus rapide sur un réseau de neurones entrainé pour autre chose. Par exemple, un réseau de neurones entrainé pour reconnaitre les chiens s’entrainera plus facilement à reconnaitre les chats.
Configuration de Python
Si ce n’est pas le cas, vous pouvez télécharger et installer Python 3
Vous pouvez ensuite installer les librairies nécessaires OpenCV, numpy et imutils
N.B.: Lorsqu’on utilise certains paquets python comme TensorFlow, il peut y avoir très fréquemment des problèmes de compatibilité. Si vous avez des difficultés n’hésitez pas à installer des versions spécifiques des paquets. Si vous avez plusieurs projets en cours, je vous invite fortement à créer des environnements virtuels (venv).
Téléchargez le fichier mscoco_label_map.pbtxt qui contient les identifiants des objets reconnus par le modèle.
N.B.: dans un précédent tutoriel, nous avions utilisez le résultat du framework Caffe pour le modèle (fichiers prototxt et caffemodel). Dans cet article, nous utilisons le SavedModel de TensorFlow.
Créer un dossier TensorFlowCV, qui sera votre espace de travail. Dans ce dossier créer un dossier pretrained_models dans lequel vous pouvez décompresser le modèle téléchargé.
Vous pouvez également créer un dossier data, dans lequel vous placerez vos images ou vidéo.
Placer les fichiers du modèle dans un dossier et créer le fichier ObjectRecognition.py
Script Python pour la reconnaissance d’Objet
Tout d’abord, nous créons un flux vidéo (vs) à l’aide de la librairie imutils qui va récupérer les images de la caméra.
vs = VideoStream(src=0, resolution=(1600, 1200)).start()
Nous initialisons un réseau de neurones avec les paramètres du SSD-ModelNetV2 (net) à l’aide de la librairie TensorFlow.
Une fonction se charge de récupérer les noms des classes reconnues par le modèle à partir du fichier pbtxt
#load class names
def read_label_map(label_map_path):
item_id = None
item_name = None
items = {}
with open(label_map_path, "r") as file:
for line in file:
line.replace(" ", "")
if line == "item{":
pass
elif line == "}":
pass
elif "id" in line:
item_id = int(line.split(":", 1)[1].strip())
elif "display_name" in line: #elif "name" in line:
item_name = line.split(":", 1)[1].replace("'", "").strip()
if item_id is not None and item_name is not None:
#items[item_name] = item_id
items[item_id] = item_name
item_id = None
item_name = None
return items
class_names=read_label_map("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/mscoco_label_map.pbtxt")
class_colors = np.random.uniform(0, 255, size=(len(class_names), 3))
Nous allons, ensuite, créer une boucle qui à chaque itération va lire l’image de la caméra et la passer en entrée du réseau de neurone pour faire la détection et reconnaissance d’objet.
#Main loop
while True:
# Get video sttream. max width 800 pixels
#img = vs.read()
img= cv2.imread('./data/two-boats.jpg') #from image file
#ret, img=vc.read() #from video or ip cam
img = imutils.resize(img, width=800)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# get height and width of image
h, w, _ = img.shape
input_tensor = np.expand_dims(img, 0)
# predict from model
resp = model(input_tensor)
Enfin, le code affiche sur l’image la boite de détection, la probabilité de reconnaissance ainsi que la position
Code complet de reconnaissance d’objet avec OpenCV et TensorFlow
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ObjectRecognitionTFVideo.py
# Description:
# Use ModelNetV2-SSD model to detect objects on image or video
#
# www.aranacorp.com
# import packages
import sys
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import cv2
import tensorflow as tf
# load model from path
model= tf.saved_model.load("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/saved_model")
print("model loaded")
#load class names
def read_label_map(label_map_path):
item_id = None
item_name = None
items = {}
with open(label_map_path, "r") as file:
for line in file:
line.replace(" ", "")
if line == "item{":
pass
elif line == "}":
pass
elif "id" in line:
item_id = int(line.split(":", 1)[1].strip())
elif "display_name" in line: #elif "name" in line:
item_name = line.split(":", 1)[1].replace("'", "").strip()
if item_id is not None and item_name is not None:
#items[item_name] = item_id
items[item_id] = item_name
item_id = None
item_name = None
return items
class_names=read_label_map("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/mscoco_label_map.pbtxt")
class_colors = np.random.uniform(0, 255, size=(len(class_names), 3))
if __name__ == '__main__':
# Camera initialisation
print("Start Camera...")
vs = VideoStream(src=0, resolution=(1600, 1200)).start() #from usb cam
#vs = VideoStream(usePiCamera=True, resolution=(1600, 1200)).start() #from RPi cam
#vc = cv2.VideoCapture('./data/Splash - 23011.mp4') #from video file
time.sleep(2.0)
fps = FPS().start()
#Main loop
while True:
#get image
img = vs.read() # Get video stream
#img= cv2.imread('./data/two-boats.jpg') #from image file
#ret, img=vc.read() #from video or ip cam
#process image
img = imutils.resize(img, width=800) #max width 800 pixels
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# get height and width of image
h, w, _ = img.shape
input_tensor = np.expand_dims(img, 0)
# predict from model
resp = model(input_tensor)
# iterate over boxes, class_index and score list
for boxes, classes, scores in zip(resp['detection_boxes'].numpy(), resp['detection_classes'], resp['detection_scores'].numpy()):
for box, cls, score in zip(boxes, classes, scores): # iterate over sub values in list
if score > 0.61: # we are using only detection with confidence of over 0.6
ymin = int(box[0] * h)
xmin = int(box[1] * w)
ymax = int(box[2] * h)
xmax = int(box[3] * w)
# write classname for bounding box
cls=int(cls) #convert tensor to index
label = "{}: {:.2f}%".format(class_names[cls],score * 100)
cv2.putText(img, label, (xmin, ymin-10), cv2.FONT_HERSHEY_SIMPLEX, 1, class_colors[cls], 1)
#display position
X= (xmax+xmin)/2
Y= (ymax+ymin)/2
poslbl= "X: ({},{})".format(X,Y)
cv2.circle(img, (int(X)-15, int(Y)), 1, class_colors[cls], 2)
cv2.putText(img, poslbl, (int(X), int(Y)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, class_colors[cls], 2)
# draw on image
cv2.rectangle(img, (xmin, ymin), (xmax, ymax), class_colors[cls], 4)
# Show video frame
cv2.imshow("Frame", img)
key = cv2.waitKey(1) & 0xFF
# Exit script with letter q
if key == ord("q"):
break
# FPS update
fps.update()
# Stops fps and display info
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
cv2.destroyAllWindows()
vs.stop()
#vc.release()
Sources d’image pour la détection d’objet
Vous pouvez utiliser ce script avec différentes sources d’image. Pour cela, il faut légèrement adapter le code précédent afin de modifier la variable « img » contenant l’image à analyser.
La webcam de votre ordinateur
vs = VideoStream(src=0, resolution=(1600, 1200)).start()
while True:
frame = vs.read()
Le stream vidéo doit être arrêté à la fin du script avec vs.stop()
vc = cv2.VideoCapture('rtsp://user:password@ipaddress:rtspPort')
while True:
ret, frame=vc.read() #from ip cam
Veillez à arrêter la capture vidéo à la fin du script avec vc.release()
La Picam du Raspberry Pi
vs = VideoStream(usePiCamera=True, resolution=(1600, 1200)).start()
while True:
frame = vs.read()
Pensez à arrêter le stream à la fin du script avec vs.stop()
Un fichier vidéo
vc = cv2.VideoCapture('./img/Splash - 23011.mp4') #from video
while True:
ret, frame=vc.read() #from video
Un fichier image
frame= cv2.imread('./img/two-boats.jpg')
Résultats
Pour cet exemple nous envoyons en entrée du réseau de neurones une image de deux bateaux qui sont reconnus correctement. Pour obtenir des résultats légèrement différents, vous pouvez modifier le paramètre confidence pour éviter les faux positifs.
Vous pouvez tester ce code avec votre webcam ou avec des photos, par exemple, pour voir les performances du modèle et de la reconnaissance d’objet
Une fois que votre script fonctionne, vous pouvez entrainer votre modèle pour qu’il puisse détecter d’autres objets.
Paquets et Modèles
Dans ce tutoriel, nous avons utiliser le modèle pré-entrainé SSD ModelNetV2. Il est bon de noter qu’ils existent d’autres modèles de classification avec des performances et caractéristiques différentes.
vgg16
vgg19
resnet50
resnet101
resnet152
densenet121
densenet169
densenet201
inceptionresnetv2
inceptionv3
mobilenet
mobilenetv2
nasnetlarge
nasnetmobile
xception
N’hésitez pas à nous laisser un commentaire pour partager les modèles que vous utilisez ou connaissez et votre retour d’expérience.
Nous présentons ici une méthode pour faire une sauvegarde légère et restaurer un Raspberry Pi. Créer une sauvegarde, image de votre Raspberry Pi, est primordiale en cas de corruption de la carte SD ou défaut d’alimentation. Il arrive souvent sur les Raspberry Pi et autres ordinateur monocarte que la carte SD fasse défaut. Il faut donc faire une sauvegarde sous la forme d’un fichier image ou iso afin de pouvoir téléverser cette image sur une nouvelle carte SD et retrouver votre configuration.
Le problème est que la sauvegarde d’une image inclue toute la mémoire de la carte SD. Vous pouvez donc vous retrouver avec plusieurs fichiers de 32, 64 ou 128Gb.
Matériel
Raspberry Pi
Deux cartes SD
Une connexion internet
Méthode de sauvegarde d’OS
Il n’y a pas d’autre méthode aussi direct. La méthode que je vous propose consiste en différentes étapes mais vous permettra d’avoir une sauvegarde légère du Raspberry Pi et de pouvoir mettre à jour vos projets facilement.
La méthode consiste en ces différents points
Récupérer les informations de l’OS
Récupérer les info hardware (optionnel)
Récupérer les fichier de configuration
Récupérer les paquets installés
Récupérer les paquets Python installé
La liste n’est certainement pas complète mais avec cela, vous pourrez vous rapprochez d’une copie presque exact de ce que vous aviez
Nettoyer votre installation
sudo apt-get --purge -y autoremove
Vous pouvez aussi supprimer tous les logiciels dont vous ne vous êtes jamais servi
Créer un dossier de sauvegarde
Il peut prendre la forme que vous voulez. L’important est qu’il soit clair afin de vous y retrouver
RPiBckp
version
projects
config
Récupérer les informations de l’OS
Selon votre projet, il est important de connaitre la version de l’OS en effet suivant les paquets utilisées des problème de compatibilités peuvent se poser
Il peut être intéressant de créer un dépôt git pour chacun de vos projets soit pour la configuration d’un Raspberry Pi, soit pour un ensemble de script Python. Un projet sous git sera idéal pour faire évoluer vos projet notamment en terme de version de paquets et d’OS.
Une fois que vous avez récupéré tous les fichiers d’intérêts, il est temps de restaurer votre projet.
Attention: faites bien attention à la version que vous installez. La version de Kernel ne doit être modifiée que par des personnes expérimentées qui ont un soucis de compatibilité qui ne peut être résolue d’une autre manière.
Copier votre dossier de sauvegarde
Sur l’installation fraiche de Raspbian, vous pouvez copier votre dossier de sauvegarde et/ou cloner le dépôt Git. Vous pouvez ensuite installer les paquets et copier les fichiers de votre configuration précédente.
Installer les paquets requis
Il est possible d’installer un ensemble de paquet APT à partir d’un fichier texte contenant la liste des fichiers à installer
xargs -a packages.txt sudo apt-get install -y
Il est possible de faire la même chose pour installer les paquets Python
python3 -m pip install -r requirements.txt
Recopier les fichiers de configuration
Pour le fichier de configuration du wifi ou tout autre fichiervous pouvez utiliser la commande de copy ou de déplacement en mode super utilisateur (sudo)
sudo cp wpa_supplicant.conf /etc/wpa_supplicant/
Conclusion
Cette méthode ne permet pas d’obtenir une copie parfaite de votre projet mais elle permet de reproduire et de maintenir votre projet avec peu d’espace mémoire ce qui peut être un énorme avantage. Vous vous retrouverez ainsi avec un ensemble de fichier de configuration qui vous permettrons rapidement de restaurer une carte SD corrompue.
Si vous avez des idées de fichiers importants à sauvegarder ou d’autres méthodes pour restaurer une carte SD, n’hésitez pas à laisser un commentaire.
La carte Rock Pi S de chez Radxa peut tourner avec Debian, Armbian, Ubuntu ou Android. Nous allons voir dans ce tutoriel comment configurer votre micro-ordinateur avec Debian.
Matériel
Rock Pi S
Carte Micro SD
Câble USB
Ordinateur/PC
Installer l’OS
Tout d’abord, il faut installer une application qui nous permettra de flasher l’OS sur la carte SD inséré sur votre ordinateur. Vous pouvez installer balenaEtcher (n’oubliez pas de choisir le système d’exploitation de votre ordinateur):
Nous allons télécharger un OS sous forme d’image à flasher sur votre carte SD. Télécharger une image à flasher tel que Debian par exemple.
Après avoir installé balenaEtcher, lancez l’application et flasher votre OS sur votre carte SD. Cela peut prendre du temps :
Une fois l’OS installé, insérez la carte SD dans le Rock Pi S.
Se connecter en SSH
Pour cela, il vous faudra télécharger un logiciel d’émulation de terminal tel que PuTTY ou TeraTerm.
Ensuite il faudra brancher votre carte ainsi :
Une fois connecté, ouvrez PuTTY et connecter en SSH de la façon suivante :
Se connecter en liaison série
Pour se connecter en liaison, on ne peut pas se connecter via les ports USB et USB-C de la carte, ce qui est fort dommage. Il faut alors utiliser un adaptateur USB UART-TTL. Pour ce tuto, nous utiliserons celui de la marque Rtinle. Brancher la carte de la façon suivante :
Une fois branché, ouvrez TeraTerm et connectez vous en liaison série, avec les paramètres suivants :
Configuration de l’OS
Une fois connecté en série ou en SSH, il faudra vous connecter avec l’utilisateur par défaut avec votre émulateur de terminal :
Default User :
rock
Default Password :
rock
Ensuite, vous pouvez lancer les commandes suivantes pour mettre votre OS à jour :
sudo apt-get update && sudo apt-get upgrade
Lire le journal système :
Il est très intéressant lorsque vous rencontré un problème de venir vérifier les messages système :
journalctl
Il est possible de passer une chaine de caractère en argument pour filtrer ce qui est affiché :
journalctl -u error
Se connecter au Wifi
Pour vérifier que votre carte réseau est bien détecté :
ip addr
Pour accéder au super-utilisateur :
sudo su
Pour scanner les réseaux WIFI disponibles :
nmcli dev wifi
Pour se connecter à votre réseau WIFI (remplacez bien wifi_name par le nom de votre réseau et wifi_password par son mot de passe ) :
nmcli dev wifi connect "wifi_name" password "wifi_password"
Installer un IDE Python
Un des langage de base utilisé sur Linux est Python. Vous pouvez installer l’IDE Thonny très simplement à l’aide la commande ( une fois connecté au wifi )
Nous allons voir dans ce tutoriel comment configurer le firmware GRBL 0.9 pour Ramps 1.4 avec Arduino Mega 2560 et s’interfacer avec LaserGRBL. Pour contrôler votre CNC ou MPCNC avec LaserGRBL, vous avez besoin d’installer et de configurer un Firmware sur votre carte Arduino, pour qu’elle puisse interpréter correctement les ordres et commandes de votre software de gravure/découpe Laser, qu’on appelle un émetteur G-code ( G-code étant le langage de programmation pour les machines CNC ). Dans ce tutoriel nous aborderons LaserGRBL
Avant de de télécharger et téléverser GRBL sur votre carte, il faut s’assurer que toutes les valeurs stockées sont nettoyées, et pour cela on a besoin du code suivant pour remettre les paramètres par défaut de votre carte :
/*
* EEPROM Clear
*
* Sets all of the bytes of the EEPROM to 0.
* Please see eeprom_iteration for a more in depth
* look at how to traverse the EEPROM.
*
* This example code is in the public domain.
*/
#include <EEPROM.h>
void setup() {
// initialize the LED pin as an output.
pinMode(13, OUTPUT);
/***
Iterate through each byte of the EEPROM storage.
Larger AVR processors have larger EEPROM sizes, E.g:
- Arduino Duemilanove: 512 B EEPROM storage.
- Arduino Uno: 1 kB EEPROM storage.
- Arduino Mega: 4 kB EEPROM storage.
Rather than hard-coding the length, you should use the pre-provided length function.
This will make your code portable to all AVR processors.
***/
for (int i = 0 ; i < EEPROM.length() ; i++) {
EEPROM.write(i, 0);
}
// turn the LED on when we're done
digitalWrite(13, HIGH);
}
void loop() {
/** Empty loop. **/
}
Brancher votre carte Arduino en liaison série avec son câble USB, copier et coller ce code dans votre Arduino IDE et puis uploader pour nettoyer l’EEPROM.
Modification de GRBL pour Ramps
Vous pouvez télécharger GRBL for Ramps sur ce Repository Github. Si vous ne savez pas utiliser Git, vous pouvez suivre ce tutoriel pour avoir les bases de cet outil.
Le Firmware GRBL a été modifié à certains endroits pour qu’il soit adapté à notre MPCNC. Avant d’uploader le Firmware avec Arduino IDE, voici les paramètres à vérifier par rapport à votre installation :
// Enables variable spindle output voltage for different RPM values. On the Arduino Uno, the spindle
// enable pin will output 5V for maximum RPM with 256 intermediate levels and 0V when disabled.
// NOTE: IMPORTANT for Arduino Unos! When enabled, the Z-limit pin D11 and spindle enable pin D12 switch!
// The hardware PWM output on pin D11 is required for variable spindle output voltages.
#define VARIABLE_SPINDLE // Default disabled. Uncomment to enable.<----------------------------------- Uncommented for Ramps 1.4
Ici, on décommente la ligne #define VARIABLE_SPINDLE dans le fichier config.h pour avoir le contrôle en PWM sur le laser.
/*-----------------------------------------------------------suite du code---------------------------------------------------*/
// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRH
#define SPINDLE_ENABLE_PORT PORTH
#define SPINDLE_ENABLE_BIT 6 // MEGA2560 Digital Pin 9 <---------- Modified for Ramps 1.4. Orignal was BIT 3 for Digital Pin 6
#define SPINDLE_DIRECTION_DDR DDRE
#define SPINDLE_DIRECTION_PORT PORTE
#define SPINDLE_DIRECTION_BIT 3 // MEGA2560 Digital Pin 5
// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
#define COOLANT_FLOOD_DDR DDRH
#define COOLANT_FLOOD_PORT PORTH
#define COOLANT_FLOOD_BIT 5 // MEGA2560 Digital Pin 8
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR DDRH
#define COOLANT_MIST_PORT PORTH
#define COOLANT_MIST_BIT 3 // MEGA2560 Digital Pin 6 <---------- Modified for Ramps 1.4. Orignal was BIT 6 for Digital pin 9
/*-----------------------------------------------------------suite du code--------------------------------------------------*/
Ici, dans le fichier intitulé cpu_map.h, il faut bien faire attention aux pins de sortie pour le « Spindle » qui va ici correspondre à la pin pour activer ou désactiver le Laser.
#define SPINDLE_PWM_DDR DDRB // <------------------------------ Modified for Ramps 1.4. Orignal was DDRH
#define SPINDLE_PWM_PORT PORTB // <--------------------------- Modified for Ramps 1.4. Orignal was PORTH
#define SPINDLE_PWM_BIT 5 // MEGA2560 Digital Pin 11 <--------- Modified for Ramps 1.4. Orignal was BIT 6 for Digital Pin 9
#endif // End of VARIABLE_SPINDLE
Configuration du pinout de la carte
Toujours dans le même fichier, cpu_map.h, On modifie la pin de contrôle de la puissance du laser en PWM pour correspondre à notre câblage.
Pour comprendre un peu comment ces modifications, il faut qu’on se penche sur les schémas structurels de la carte Ramps 1.4 et de l’Arduino MEGA 2560 :
On peut remarquer que les pins digitals qui nous servent à alimenter le laser (Pin Digital 9) et celle qui sert à le contrôler (Pin Digital 11) sont définis à travers leur PORT, d’où la nécessité de les définir par leurs numéros de Port, respectivement 6 sur le PORTH et 5 sur le PORTE (PH6 et PE5 sur le schéma). La modification PH3 pour la pin digital 6 sert juste à libérer la pin digital 9 qui utilisé de base pour un système de refroidissement qui n’est pas dans notre configuration.
Maintenant que vous avez compris les modifications apportés au Firmware, vous pouvez l’uploader sur votre carte Arduino. Attention, la compilation peut prendre plusieurs minutes, c’est normal, soyez patient.
Menu de configuration GRBL
Une fois LaserGRBL installé, ouvrez l’application puis connectez vous en liaison série USB à votre carte Arduino Mega / votre CNC :
Ensuite, cliquez sur Grbl Configuration, qui correspond aux paramètres propres à la CNC qui est connectée. Vous ne pouvez que modifier et réécrire les valeurs lorsque LaserGRBL est connecté à votre CNC.
Vous aurez accès aux paramètres de votre CNC , valeurs qui seront enregistrés sur votre carte Arduino.
N.B.: si vous utilisez plusieurs machines ou plusieurs ordinateurs, vous pouvez exporter et importer vos paramètres de configuration
Désactivation des limites de position
Tout d’abord, commencez par désactiver les Soft Limits en mettant à 0 la valeur de la ligne Soft limits enable (&20 et $21). Cela rendra les premiers tests plus simples sans que LaserGRBL pense que votre machine se trouve en dehors de ses dimensions.
Configuration des paramètres
Les paramètres importants ici tous ceux qui concernent les axes X, Y et Z. Pour régler la valeur de travel resolution, aussi dit la résolution de déplacement de chaque axe, il faut se référer à l’équation : Résolution de déplacement = (Pas par tour * Réduction mécanique) / Micro-pas
Pas par tour (Step per Revolution) : C’est le nombre de pas effectués par le moteur pour effectuer une rotation complète.(ici 200pas par tour)
Réduction mécanique (Gear Reduction) : Si votre système utilise des engrenages ou des poulies pour réduire ou augmenter le couple, vous devez prendre en compte le rapport de réduction mécanique. Cela affectera la résolution de déplacement effective de l’axe. Nous utilisons des courroies qui ont pour valeur 40 : 1 de réduction mécanique.
Micro-pas (Microstepping) : Certains contrôleurs de moteur pas à pas prennent en charge le micro-pas, qui permet de diviser chaque pas électrique. Cela permet une meilleure résolution. Les valeurs possibles sont 1/2, 1/4, 1/8, 1/16, etc. (Ici on a 1/32).
Pour l’accélération, veuillez vous référer à la valeur donner sur la datasheet du moteur.
Notes sur le calcul des paramètres
Résolution
Pour un système poulie (20dents) courroie (pas 2mm) avec micro-pas (1/32), le rapport de réduction est donné par:
( 200 * 32 ) / (20*2) = 160 step/mm
Pour un système à vis sans fin avec un ratio de 8mm par revolution et micro-pas (1/32), on a
( 200 * 32 ) / 8 = 800 step/mm.
vitesse maximale
Pour la vitesse maximale, soit le Maximum Rate vous pouvez utiliser l’équation suivante :
Vitesse maximale = (Pas par tour * Fréquence maximale d’impulsions de pas) / (Micro-pas * 60)
Dans notre cas Vitesse maximale = ( 200*36 000) / ( 40*60 ) = 3000 mm/min.
zone de travail
La taille maximale de déplacement(maximum travel) dépend de la surface réelle de travail de votre CNC. Pour la mesurer :
Configurez tout d’abord les paramètres précédents
Avec les flèches de l’interface utilisateur de LaserGRBL, déplacez la tête de votre CNC jusqu’au coin bas gauche.
Allumez le laser avec le bouton Focus et marquez l’endroit où il pointe, en allumant le laser avec une forte puissance par exemple.
répéter l’opération dans tous les coins.
mesurez la surface de travail avec une règle ou un mètre.
Modification et ajout de bouton sur LaserGRBL
Pour modifier un bouton, il suffit de clique-droit dessus et choisissez « Editer le bouton »
Vous pouvez modifier GCode envoyer par le bouton ainsi. Pour que le Laser Focus ainsi que Blink marche à 3% de sa puissance, vous pouvez mettre ces GCodes suivant.
; pour GCode
M3 S30
G1 F1000
; pour GCode2
M5 S0
G0
Pour créer votre propre bouton, vous pouvez faire clique-droit sur une zone vide de l’interface utilisateur où il n’y a pas de bouton et choisissez « Ajouter un bouton personnalisé ». Cela peut être utile par exemple pour ajouter de quoi manipuler l’axe Z si vous voulez changer la hauteur de l’outil de votre CNC :
Entrer : permet de choisir si c’est un bouton qui s’active en cliquant une fois dessus, en maintenant appuyé, qui s’enclenche et se déclenche en cliquant dessus, etc..
Caption : ce sera le nom afficher de votre bouton
Info-bulle : permet de décrire ce que fait votre bouton
Activé : permet de savoir quand est-ce que le bouton sera débloqué, ici Idle signifie qu’il sera actif seulement si la CNC est connecté et déverrouillé.
GCode : c’est là où on met les lignes de commandes qu’enverra votre bouton à votre CNC. Voici différents GCode pour 6 boutons pour déplacer l’axe Z avec une précision différente :
G91 Z-10 ; Monter l'axe Z de 10mm
G91 Z10 ; Descendre l'axe Z de 10mm
G91 Z-5 ; Monter l'axe Z de 5mm
G91 Z5 ; Descendre l'axe Z de 5mm
G91 Z-1 ; Monter l'axe Z de 1mm
G91 Z1 ; Descendre l'axe Z de 1mm
Une MPCNC est l’acronyme de « Mostly Printed CNC », ce qui signifie qu’elle est principalement construite à partir de pièces imprimées en 3D. Il s’agit d’une machine CNC (Computer Numerical Control) qui permet de réaliser des opérations d’usinage, de gravure, de découpe ou de fraisage sur différents matériaux tels que le bois, le plastique ou l’aluminium. La MPCNC est conçue pour être abordable et facile à construire, grâce à son architecture modulaire et à l’utilisation de composants électroniques couramment disponibles. Elle est basée sur le principe de la fraiseuse à portique ou un Laser, où un chariot mobile se déplace le long de 3 axes (X et Y principalement, et Z pour régler la hauteur de l’outil légèrement ) tandis que l’outil effectue les opérations sur le matériau. La MPCNC peut être contrôlée par un logiciel de commande numérique, qui envoie des instructions précises aux moteurs pour déplacer les axes selon les coordonnées spécifiées. Ici, nous nous appuyons sur la MPCNC de V1 Engineering, pour construire notre propre Graveuse et Découpe Lase. Nous aborderons essentiellement le câblage de la Machine, le guide technique pour imprimer et monter les pièces se trouvant sur le site de V1 Engineering.
La carte Ramps 1.4 est une carte d’extension électronique utilisée dans les imprimantes 3D et les machines CNC. Le terme « Ramps » est un acronyme pour « RepRap Arduino Mega Pololu Shield ». Elle est spécifiquement conçue pour fonctionner avec la plateforme Arduino Mega et offre une interface de contrôle pour les moteurs pas à pas, les capteurs, les fins de course, les ventilateurs, etc. La carte Ramps 1.4 est dotée de plusieurs emplacements pour les pilotes de moteurs pas à pas Pololu A4988 ou DRV8825, qui fournissent la puissance nécessaire pour contrôler les moteurs de la machine CNC ou de l’imprimante 3D. Elle intègre également des connecteurs pour les thermistances, les ventilateurs de refroidissement, les afficheurs LCD, les boutons de commande et d’autres périphériques. La carte Ramps 1.4 est compatible avec divers logiciels de contrôle, des firmware, tels que Marlin ou GRBL, qui permettent de gérer précisément les mouvements des moteurs et de superviser l’impression ou l’usinage en cours.
Après avoir monté la structure de votre MPCNC, il vous faut monter la partie électronique, et pour cela, on commence avec la Ramps 1.4. Comme décrit précédemment, c’est un Shield pour carte Arduino Mega, ce qui rend le montage assez simple. Il suffit d’empiler les broches et les pins des 2 cartes :
Ensuite on vient placer les jumpers pour les drivers. Il faut relier les pins qui se trouveront sous les drivers sur la carte tel que :
Ensuite, on passe aux pilotes des moteurs pas à pas qui permettent la communication entre les moteurs et la carte. Ils ont aussi une fonction très importante de limiter le courant dans les moteurs pour éviter les surchauffes. Il suffit de les enfoncer dans les pins juste des jumpers tel que :
Réglage du potentiomètre de chaque driver
Il est TRÈS important de faire cette étape correctement, si le courant est trop élevé, cela provoque une surchauffes moteurs qui finit par les abîmer sur des longues durées. Commençons par calculer le courant maximum à laisser passer dans les moteurs. Pour cela il faut vous référer à l’équation qui limite le courant dans la datasheet de votre Pilote. Ici, pour les DRV8825, on a l’équation suivantes : Limit Current = VREF x 2 VREF est la tension qu’on mesure aux bornes du pilote et qu’on règle avec le potentiomètre et Limite Current est le Courant/Phase consommé par votre moteur, valeur indiqué dans la datasheet de votre moteur pas à pas. Ici, pour les 17HS15-1504S-X1 on Courant/Phase = 1,5 A Donc pour savoir la valeur de VREF, on a VREF = Limite Current / 2 = 1,5 / 2 = 0,75 V. Cependant, ici, nos moteurs pas à pas ne portent pas de charges lourdes, qui nécessitent davantage de courant. 0,75 V étant le maximum, on peut réduire cette valeur pour limiter la surchauffe des moteurs, à 60%. Soit VREF(diminué) = 0,75 x 0,6 = 0,45 V.
Maintenant, après avoir branché votre carte à l’alimentation 12V ( pensez bien à régler la tension de sortie de l’alimentation avec un tournevis et en mesurant la tension aux bornes des sorties ), munissez vous d’un multimètre pour mesurer VREF aux bornes du pilote comme ceci :
Vous pouvez mesurer directement VREF entre la masse du pilote et sur le potentiomètre directement. Pour régler, il vous faudra un tournevis assez fin pour manipuler la vis du potentiomètre et changer la valeur VREF à la tension souhaitée, ici 0,45V.
Branchement des moteurs pas-à-pas
Une fois votre VREF réglé, vous pouvez brancher les moteurs pas-à-pas. On remarque alors ici sur notre machine, nous avons deux moteurs pas-à-pas pour chacun des axes X et Y, ce qui nécessite de les brancher en séries pour qu’ils puissent être contrôler par le même pilote. On peut aussi les brancher en parallèle mais nous vous déconseillons fortement car cela consommerait davantage de courant.
Vous pouvez remarquer que les moteurs pas-à-pas sont branchés en séries, ce qui implique de couper des câbles et de les resouder. Les couleurs représentés sur ce schéma sont celles des couleurs de câbles usuellement vendu avec des moteurs pas-à-pas. Faites bien attention à l’ordre des câbles lors des branchements. Aussi, on peut remarquer que l’axe Y le sur schéma n’as pas les couleurs de câbles dans le même ordre que les autres, mais un ordre inversé. C’est normal, vous pouvez inverser l’ordre des couleurs branchés sur la carte Ramps 1.4 pour inverser le sens de rotation des moteurs, pour qu’ils correspondent à votre installation. Il faudra faire des tests pour savoir s’ils tournent dans le bon sens.
Branchement du Laser
Ceci est une étape qui peut varier car il dépend de l’outil que vous brancherez sur votre MPCNC. Vous pouvez y brancher une fraiseuse par exemple. Ici, nous prendrons comme exemple le branchement du Laser 20WLD4070HA. On l’alimentera en 12V par la pin D9 et on contrôlera la puissance du laser par la Pin Digital 11 en PWM.
Voilà maintenant le câblage électrique terminé ! Pour la partie software et firmware, vous pouvez suivre ce tutoriel.
Nous allons voir dans ce tutoriel comment créer et lancer un fichier Profile au démarrage d’une machine Linux, comme votre Raspberry Pi, afin de configurer et personnaliser le compte de votre utilisateur. Lorsque vous utilisez régulièrement, votre ordinateur Linux, il peut être utile de charger un profil au démarrage afin d’avoir un environnement qui vous correspond et qui peut vous suivre d’une machine à une autre.
N.B.: Pour obtenir le même fonctionnement qu’un fichier .profile, il est possible de créer un fichier que vous lancer au démarrage de votre système (dans le fichier rc.local ou à l’aide de crontab) avec la commande source ./myprofile
Qu’est-ce qu’un fichier profile?
Comme mentionné en introduction, un fichier profile va se lancer au démarrage de la machine. Il y a des fichiers de configuration propre au système (OS, hardware) qui vont se charger pour tous les utilisateurs et des fichiers de configuration propre à chaque compte utilisateurs qui vont se lancer à la connexion et permettre de configurer des alias, des fonctions, des chemins d’accès, etc.
Où trouver le fichier profile?
Le fichier profile le plus courant est caché et situé à la racine .profile. Si c’est un fichier que vous avez créé et que vous lancer au démarrage ou manuellement, vous pouvez l’enregistrer où vous le souhaitez.
Pour le trouver, vous pouvez utiliser la commande suivante qui permet d’afficher les fichiers cachés sur votre système
ls -a
ou
ls -al
Voici quelques fichiers de configuration qui se charge au démarrage
~/.profile
/etc/profile
/etc/profile.d
/etc/bashbashrc
/etc/bash_completion
Que doit-on mettre dans le fichier profile?
Le langage utilisée dans le fichier profile est le bash. Il doit donc être adapté en fonction du shell utilisé
Les alias
Lorsqu’on travaille avec Linux, on a souvent à faire au terminal et au ligne de commande. Les alias permettent de raccourcir des lignes de commandes.
alias <name>='command1 arg1 arg2; command2'
N.B.: il n’y a pas d’espace autour du « = ». On peut mettre plusieurs commande à la suite avec « ; », « && » ou « | ».
alias ll='ls -l'
Vous pouvez retrouver la liste des alias en tapant la commande alias sans argument
quelques applications pratiques
alias update='sudo apt-get update && sudo apt-get upgrade'
alias histg='history | grep' # "histg cd" list all previous commands containing cd
alias aptclean='sudo apt-get -y autoremove && sudo apt-get clean'
alias syslog='cat /var/log/syslog'
Ce fichier une fois créé, peut vous suivre d’une machine à une autre et vous permettre de retrouver votre environnement.
les fonctions
les fonctions sont comme des alias avec des séquences de commandes qui peuvent être plus complexes
cdl(){
cd "$@";
ls -al;
}
les exports
Un peu comme les alias vous pouvez créer des variables d’environnement et donner des noms pour vos chemins d’accès, par exemple