Table des matières

SAE Vision Industrielle pour les étudiants Formation Initiale

Manipulation Robotique RubiksCube

https://www.youtube.com/embed/mFfiV3QRtU8

Partie 1) Lecture d'une face du Rubik's Cube à une position connue

Dans un premier temps, vous devrez développer l'application réalisant la lecture d'UNE face du Rubik's cube, le cube étant à une position connue et maîtrisée.

Les étapes du traitement sont données ici:

L'image de test à traiter est:

Image 0: bvdp.inetdoc.net_files_iut_tp_lpro_vision_rubiks.jpg

Vous devez spécifier manuellement les coordonnées des 4 coins de la face à traiter:

Image 1:

Vous devez ensuite générer une image rectifiée (par application de l'homographie) de la face du cube avec une résolution de 31×31 pixels:

Image 2:

Vous devez ensuite déterminer pour chacun des pixels de l'image quelle est la couleur (parmi les 6 couleurs apprises) la plus proche et sinon utiliser un autre indice pour indiquer que le pixel n'est d'aucune des 6 couleurs connues:

Image 3:

Vous devez finalement effectuer un vote pour chacune des cases du rubiks cube en utilisant les différentes valeurs calculées précédemment, et générer une image de 3×3 pixels:

Image 4:

Vous devrez fournir en sortie l'information des 9 cases lues pour la face du cube traitée.

Votre programme devra également fournir un booléen indiquant si la face du cube a pu être correctement lue (chacune des cases ayant une apparence correcte).

Votre programme devra tenir compte du fait que les cases centrales Noires ou Blanches sont souvent dotée d'un logo et ne sont donc pas uniforme.

Partie 2) Lecture d'une face du Rubik's Cube à une position INconnue

Une fois la première partie terminée, il est temps de l'intégrer pour permettre de lire le cube, celui ci n'étant plus positionné de manière maîtrisée. Une à trois faces du cube peuvent être visibles par la caméra selon les positions et orientations relatives. L'objectif est ici de trouver les 4 coins de chacune des faces visible pour les lire avec le travail fait à la partie 1.

Pour cela, vous pourrez chercher à détecter des pixels candidats, situés à l'intersections de segments de droites, elles-mêmes détectées dans l'images. Vous pourrez utiliser un algorithme appelé RANSAC (RANdom SAmple Consensus)

Partie 3) Estimation de la pose du Rubik's Cube

Vous devrez ici, à partir des 4 coins de la face supérieure du cube, calculer sa pose (position et orientation par rapport à la caméra, puis par rapport au robot).

Pour cela, vous devrez avoir étalonné la caméra (estimé les paramètres intrinsèques et extrinsèques de son modèle).

Vous devrez ensuite utiliser l'algorithme Perspective N Points (PnP) pour calculer la pose du cube à partir de l'observation des 4 coins de sa face supérieure.

Images synthétiques

Fichier pour générer les facettes du cube

generecube.py
import os
import cv2
import numpy as np
 
 
#tabcolors_python= [[0,2,6,5,5,1,3,2,1],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0] ]
tabcolors_python= [[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6],[3,4,4,2,3,5,6,5,6],[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6] ]
 
 
def create_tab_colors():
    with open('tab_colors.scad', 'w') as f:
        f.write('// fichier de définition du cube, a inclure avec:\n')
        f.write('// include <tab_colors.scad>\n')
        f.write('// Definition des couleurs\n')
        f.write('color_black = [0, 0, 0]; // indice 0 \n')
        f.write('color_white = [1, 1, 1]; // indice 1\n')
        f.write('color_red = [1, 0, 0]; // indice 2\n')
        f.write('color_green = [0, 1, 0]; // indice 3\n')
        f.write('color_orange = [1, 0.5, 0]; // indice 4\n')
        f.write('color_yellow = [1, 1, 0]; // indice 5\n')
        f.write('color_blue = [0, 0, 1]; // indice 6\n')        
        f.write('tab_indices_color=[color_black,color_white,color_red,color_green,color_orange,color_yellow,color_blue];\n')
        f.write('tabcolors= [')
        for nface in range(6):
            f.write('[')
            for ncarre in range(9):
                #f.write(str(nface))
                f.write(str(tabcolors_python[nface][ncarre]))
                if ncarre!=8:
                    f.write(',')
                else:
                    f.write(']')
                    if nface!=5:
                        f.write(',')
 
        f.write('];')
        f.close()
 
create_tab_colors()

Fichier modèle 3D du cube

rubiks.scad
include <tab_colors.scad>
// Dimensions du Rubik's Cube
cube_size = 26;
interieur = 90;
interstice = 3; 
 
 
 
// Fonction pour créer un carré
module colored_square(color) {
    color(color) {
        square([cube_size, cube_size], true);
    }
}
 
 
// Fonction pour créer une face du Rubik's cube 
module face(colors) {
    for (r = [0:2]) {
        for (c = [0:2]) {
            translate([r * (cube_size + interstice), c * (cube_size + interstice), 0]) {
                colored_square(colors[r][c]);
            }
            // Ajout de l'interstice
            translate([r * (cube_size + interstice) + interstice, c * (cube_size + interstice) + interstice, 0]) {
            }
        }
    }
}
 
 
// Assemblage du Rubik's Cube 
module rubiks_cube() {
    translate([-28, -28, 1]) face([[
    tab_indices_color[tabcolors [0][0]], tab_indices_color[tabcolors [0][1]], tab_indices_color[tabcolors [0][2]]], [tab_indices_color[tabcolors [0][3]], tab_indices_color[tabcolors [0][4]], tab_indices_color[tabcolors [0][5]]], [tab_indices_color[tabcolors [0][6]], tab_indices_color[tabcolors [0][7]], tab_indices_color[tabcolors [0][8]]]]);
    ////////////////////////////////////////////////////
    //pas bon
    translate([-28, 46, 17]) rotate([90,0,0]) face( [[tab_indices_color[tabcolors [1][0]], tab_indices_color[tabcolors [1][1]], tab_indices_color[tabcolors [1][2]]], [tab_indices_color[tabcolors [1][3]], tab_indices_color[tabcolors [1][4]], tab_indices_color[tabcolors [1][5]]], [tab_indices_color[tabcolors [1][6]], tab_indices_color[tabcolors [1][7]], tab_indices_color[tabcolors [1][8]]]]);
   ////////////////////////////////////////////////////
       //pas bon
    translate([46, -28, 17])rotate([0,-90,0])face([[tab_indices_color[tabcolors [2][0]], tab_indices_color[tabcolors [2][1]], tab_indices_color[tabcolors [2][2]]], [tab_indices_color[tabcolors [2][3]], tab_indices_color[tabcolors [2][4]], tab_indices_color[tabcolors [2][5]]], [tab_indices_color[tabcolors [2][6]], tab_indices_color[tabcolors [2][7]], tab_indices_color[tabcolors [2][8]]]]);
     ////////////////////////////////////////////////////
 
    translate([-28, -44, 17]) rotate([90,0,0]) face([[tab_indices_color[tabcolors [3][0]], tab_indices_color[tabcolors [3][1]], tab_indices_color[tabcolors [3][2]]], [tab_indices_color[tabcolors [3][3]], tab_indices_color[tabcolors [3][4]], tab_indices_color[tabcolors [3][5]]], [tab_indices_color[tabcolors [3][6]], tab_indices_color[tabcolors [3][7]], tab_indices_color[tabcolors [3][8]]]]);
    ////////////////////////////////////////////////////
 
    translate([-44, -28, 17])rotate([0,-90,0]) face([[tab_indices_color[tabcolors [4][0]], tab_indices_color[tabcolors [4][1]], tab_indices_color[tabcolors [4][2]]], [tab_indices_color[tabcolors [4][3]], tab_indices_color[tabcolors [4][4]], tab_indices_color[tabcolors [4][5]]], [tab_indices_color[tabcolors [4][6]], tab_indices_color[tabcolors [4][7]], tab_indices_color[tabcolors [4][8]]]]);
    ////////////////////////////////////////////////////
       //pas bon
    translate([-28, -28,91]) face(    [[tab_indices_color[tabcolors [5][0]], tab_indices_color[tabcolors [5][1]], tab_indices_color[tabcolors [5][2]]], [tab_indices_color[tabcolors [5][3]], tab_indices_color[tabcolors [5][4]], tab_indices_color[tabcolors [5][5]]], [tab_indices_color[tabcolors [5][6]], tab_indices_color[tabcolors [5][7]], tab_indices_color[tabcolors [5][8]]]]);
    ////////////////////////////////////////////////////
 
}
 
module black_cube(color) {
    color([0, 0, 0]) {
        translate([1,1,46]) cube([interieur, interieur, interieur],center = true);
    }
}
 
// Appel de la fonction pour générer le cube noir
black_cube();
rubiks_cube();

Fichier pour générer une image

genere_img.py
import os
import cv2
import numpy as np
 
filename_img="img"
#executable_openscad="openscad"
executable_openscad="D:\openscad-2021.01\openscad.exe"
 
chaine=f'{executable_openscad} -o {filename_img}.png --imgsize=1024,768 --camera=230,340,500,0,0,0 --projection=p rubiks.scad'
os.system(chaine)

Code pour générer des images aléatoires

genere_img_aleatoire.py
import os
import cv2
import numpy as np
import time
import random
#tabcolors_python= [[0,2,6,5,5,1,3,2,1],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0] ]
tabcolors_python= [[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6],[3,4,4,2,3,5,6,5,6],[5,2,3,5,1,1,6,5,6],[5,2,3,4,1,1,6,5,6],[5,2,2,4,2,1,5,5,6] ]
 
def create_image():
 
 
    def create_tab_colors():
        with open('tab_colors.scad', 'w') as f:
            f.write('// fichier de définition du cube, a inclure avec:\n')
            f.write('// include <tab_colors.scad>\n')
            f.write('// Definition des couleurs\n')
            f.write('color_black = [0, 0, 0]; // indice 0 \n')
            f.write('color_white = [1, 1, 1]; // indice 1\n')
            f.write('color_red = [1, 0, 0]; // indice 2\n')
            f.write('color_green = [0, 1, 0]; // indice 3\n')
            f.write('color_orange = [1, 0.5, 0]; // indice 4\n')
            f.write('color_yellow = [1, 1, 0]; // indice 5\n')
            f.write('color_blue = [0, 0, 1]; // indice 6\n')        
            f.write('tab_indices_color=[color_black,color_white,color_red,color_green,color_orange,color_yellow,color_blue];\n')
            f.write('tabcolors= [')
            for nface in range(6):
                f.write('[')
                for ncarre in range(9):
                    #f.write(str(nface))
                    f.write(str(tabcolors_python[nface][ncarre]))
                    if ncarre!=8:
                        f.write(',')
                    else:
                        f.write(']')
                        if nface!=5:
                            f.write(',')
 
            f.write('];')
            f.close()
 
    create_tab_colors()
 
    time.sleep(2)
 
    filename_img="img"
    for j in range(9):
      filename_img=filename_img+str(tabcolors_python[5][j])
    #executable_openscad="openscad"
    executable_openscad="D:\OpenSCAD-2021.01-x86-64\openscad-2021.01\openscad.exe"
 
    chaine=f'{executable_openscad} -o {filename_img}.png --imgsize=1024,768 --camera=230,340,500,0,0,0 --projection=p rubiks.scad'
    os.system(chaine)
######################################################
for i in range(7):
    print (str(i))
    #tabcolors_python[5][0]=i
 
    for j in range(9):
        tabcolors_python[5][j]=random.randint(0, 7)
    create_image()

Images Réelles

Pleins d'images à utiliser: https://bvdp.inetdoc.net/files/iut/vision_BUT3/

Archive zip des images: https://bvdp.inetdoc.net/files/iut/vision_BUT3/images_rubiks.zip

Codage HSV en openCV

Teinte en opencv va de 0 à 180 (code des 2 degrés)

hsv.py
import numpy as np
import cv2
import math
import os
 
if __name__ == '__main__':
 
    im=np.zeros((1,1,3),np.uint8)
    im[:,:,:]=(74,188,41)
 
    im[:,:,:]=(255,255,255)
    im[:,:,:]=(255,0,0)
    im[:,:,:]=(0,255,0)
    im[:,:,:]=(0,0,255)
 
    hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
    print(hsv)
    exit(0)

Image sur fond rose