=====Méthode de résolution manuelle avec animations===== https://www.francocube.com/cyril/rubiks-cube =====Simulateur Rubik's===== https://github.com/davidwhogg/MagicCube https://jakevdp.github.io/blog/2012/11/26/3d-interactive-rubiks-cube-in-python/ cd ~/pythonb/ mkdir rubiks_3D cd rubiks_3D git clone https://github.com/davidwhogg/MagicCube.git cd MagicCube/code /usr/bin/idle3 cube_interactive.py & =====Détails manip AIP===== video de résolution de rubiks cube par des robots: https://www.youtube.com/watch?v=GJKdbGAF9H0 IP raspberry pi en ethernet: ssh -X pi@192.168.3.11 password: RPiS3ns3H4t ssh pi@192.168.3.11 #Run the sensor server ./bvdp/opencv_rubiks/multi_server_sensors.py #Calibrate the camera: slogin -Y pi@192.168.3.11 ./bvdp/opencv/camera3.py ./bvdp/opencv/camera4.py #Run the app ./bvdp/opencv/tcv4.py =====Présentation du matériel===== ====Règles de sécurité==== Il est absolument nécessaire d’observer les quelques règles de sécurité suivantes afin d'éviter tout incidents pendant les phases de manipulation. * Toute manipulation du robot doit s'opérer en binôme et sous surveillance de l’encadrant. * Ne jamais pénétrer dans la zone de travail du robot lorsqu'il est en fonctionnement, et toujours travailler avec la cage de protection fermée. * En cas d'incident, appuyer sur l'arrêt d'urgence situé sur le SmartPAD ou la grille de protection. * Ne pas toucher le robot après ou pendant le fonctionnement. Les moteurs peuvent atteindre des températures élevés. * On se limitera toujours à une vitesse < 20% pendant les tests des programmes. On ne dépassera jamais 50% de la vitesse maximale. ====Système robotique===== Le robot KR 6 R700 sixx est un bras manipulateur industriel. Il dispose de 6 degrés de libertés, et, dans sa configuration à l’AIP, est équipé d’une pince pneumatique en guise d’organe terminal. Le système robotique complet se compose de quatre éléments: * Le bras manipulateur KR6 R700 * L’armoire de commande * Le Kuka control panel ou Smartpad * La cage de protection ====Logiciels===== Les différents logiciels utilisés pour simuler et programmer le robot sont: * **KUKA.SimPro** (Simulation du robot, programmation simplifiée) * **KUKA.OfficeLite** (Programmation complete du robot en simulation) * **KUKA.VisualWork** (Edition des programmes et configurations du robot, transfert avec le robot) La plupart des informations requises pour programmer un robot KUKA sont disponibles dans le manuel suivant: [[http://supportwop.com/IntegrationRobot/content/4-Logiciel_Utilisation_Programmation/Logiciel_systeme_KSS_VSS/KSS8x/8.2/Manuel_de_service__int%C3%A9grateur_KSS_82_fr.pdf|Manuel de service intégrateur ]] __**Il servira de référence pour l'ensemble des travaux pratiques à réaliser.**__ Le langage de programmation des robots KUKA est le KRL (KUKA ROBOT LANGUAGE). Plusieurs niveaux de programmation sont possibles pour programmer les robots KUKA en fonction du groupe utilisateur: ==Programmation pour le groupe d'utilisateur "Utilisateur" (formulaires en ligne)== Pour les instructions souvent utilisées, telles que des déplacements simples, on dispose de formulaires en ligne dans le KSS. Ceci facilite la programmation. ==Programmation pour le groupe d'utilisateur "Expert" (syntaxe KRL)== Le mode utilisateur Expert donne accès à l’entièreté de la syntaxe KRL. ====Lancement des applications==== * Lancer Kuka Sim Pro avec l'icone sur le bureau * Cliquer sur "e-Catalogue" en bas à gauche (si le bouton n'apparait pas, cliquer sur l'onglet "Modélisation", puis à droite sur menu déroulant Afficher, cocher "e-Catalogue" ) * Dans Collection, développer "Modèles publics"->"Kuka Sim ..."->"KUKA_ROBOT"->"Small Robots(3kg...)"-> Archiv->AGILUS-A Series->sixxn et choisir en double cliquant sur "KR 6 R700 sixx", le robot apparaît dans la vue 3D, puis à droite dans propriété, dans CodeGenTemp..., selectionner KSS8.3 (c'est la version du controleur) * Lancer VMware Workstation sur le bureau, cliquer sur continu, open virtual machine, choisir "D:\vm_pour_office_lite_kuka\00216116;01;KUKA.....\V_83OLK_0007\KR C...." * Cliquer sur l'icone PLAY verte pour démarrer la machine virtuelle. * Dans la machine virtuelle, vous retrouver bureau d'une machine sous windows. Positionner la fenêtre virtuelle à droite sur votre écran pour ne pas confondre les applicatifs fonctionnant sur la machine hôte et sur la machine virtuelle. * Dans la machine virtuelle, cliquer sur l'icone Kuka en bas, puis saisir l'adresse du serveur FLEX: 192.168.0.235 , et ignorer d'éventuelles erreurs * Cliquer sur Démarrer, Kuka, StartKRC et rentrer à nouveau l'adresse du serveur Flex, et ignorer d'éventuelles erreurs * Cliquer sur Démarrer, clic droit sur Computer, puis propriété, dans computer name, cliquer sur Change settings, copier la valeur du "Full Computer Name" pour récupérer le nom de la machine virtuelle, ce qui permettra ensuite à Kuka Sim Pro de s'y connecter * Sur la machine hote, dans Kuka Sim Pro, cliquer sur l'onglet Programme, puis en bas à gauche, cliquer sur Controller Map, , développer "KSS 8.3, KR6 R 700 sixx", "VRC Coupé" , et en haut cliquer sur l'icone Connecter. Dans la fenêtre qui apparaît, copier le nom de la machine WINDOWS-BUR36IOR , entrée puis connecter. * Ensuite, une fenêtre pour selectionner le programme apparait, choisir "masref_user" =====Présentation de l'application à réaliser===== On se propose au cours de ce TP de résoudre un Rubik's cube à l'aide du bras KUKA. Le robot est équipé d'une pince pneumatique permettant de saisir le cube. De plus un support fixe sera placé dans l'espace de travail pour que le robot puisse faire tourner les deux premières rangés de cubies (facette d'un Rubik's cube). {{http://homepages.laas.fr/tflayols/files/ressources_kuka/support_cube.PNG|}} Par un ensemble de manipulation à définir, le robot sera capable de faire tourner le cube et le replacer dans dans le socle dans toutes les orientations. Ainsi, par déplacements successifs, le robot disposera des mouvements de base pour entamer la résolution du Rubik's cube. Un système de vision permet d'observer la face supérieure du cube et met à disposition les couleurs des 9 cases sur un serveur de capteurs (Serveur TCP). Un programme python se charge de donner les ordres de déplacement au robot KUKA sur demande. Dans un premier temps, ce programme récupère les configurations de chaque face du cube en donnant les ordre de déplacements au robot permettant de présenter les différentes faces à la caméra. Une fois les 6 faces connues, l'application fait appel à un algorithme de résolution [[https://github.com/Wiston999/python-rubik]]. Finalement, les consignes de résolutions sont envoyées au robots au fur et à mesure de leurs exécutions. ==== Notations ==== Pour résoudre un Rubik's cube, un ensemble de notation standards ont été définies. C'est sous cette forme que les algorithmes de résolution donnent les séquence de mouvement à réaliser. Elles expriment les mouvement en considérant l'orientation du robot dans le monde (faces UP, DOWN, LEFT, RIGHT, FRONT et BACK). Avec notre système robotique, nous sommes obligés de tourner le cube pour présenter la face à faire tourner vers le bas. Aussi dans l'ensemble des programmes, on appellera les faces non pas par leurs orientations géométriques, mais par leurs couleurs centrale. (Red, Yellow, Orange, Blue, While, Green). Les ordres de résolutions sont alors, une couleur de face, et un sens de rotation horaire (CW - ClockWise) ou anti horaire (CCW - ConterClockWise). L'application de résolutions traduit la rotation d'une face (Rouge par exemple) par des rotations de l'ensemble du cube, pour placer la face rouge dans le socle, puis une rotation des deux rangés supérieures. Mouvements de base: La résolution du Rubik's cube est traduite en mouvements de base qui sont: * code 0 - **FCW** : Tourner l'ensemble du cube dans le sens horaire en regardant la face FRONT (avant) * code 1 - **FCCW** : Tourner l'ensemble du cube dans le sens anti-horaire en regardant la face FRONT (avant) * code 2 - **UCW** : Tourner l'ensemble du cube dans le sens horaire en regardant la face UP (du haut) * code 3 - **UCCW** : Tourner l'ensemble du cube dans le sens anti-horaire en regardant la face UP (du haut) * code 4 - **MCW** : En bloquant la face DOWN (du bas), tourner les deux rangés du haut dans le sens horaire * code 5 - **MCCW** : En bloquant la face DOWN (du bas), tourner les deux rangés du haut dans le sens anti-horaire Où le **code** est une valeur entière transmise au robot Un dernier code transmit comme un mouvement permet d'informer le robot que la manipulation est finie * code 100 - **END** : Tous les mouvements ont été transmis, fin du programme KRL A l'initialisation, le cube doit être placé avec le centre de chaque face dans la configuration suivante: * FRONT center -> RED * RIGHT center -> GREEN * UP center -> YELLOW * ... {{http://homepages.laas.fr/tflayols/files/ressources_kuka/setup_kuka.jpg|}} ===== Annexes ===== ====KUKA.Si Pro 3.04==== Modalisation de la pince: {{http://homepages.laas.fr/tflayols/files/ressources_kuka/Kuka%20Sim%20Pro%203.docx|Doc_modelisation}} {{http://homepages.laas.fr/tflayols/files/ressources_kuka/Kuka_Gripper.vcmx|Kuka_Gripper.vcmx}} ===Configuration de licence=== {{http://homepages.laas.fr/tflayols/files/ressources_kuka/01.PNG|}} {{http://homepages.laas.fr/tflayols/files/ressources_kuka/02.PNG|}} ===Activation de l'option "connectivity"=== File->Option, Add On, cliquer sur Enable à droite de Connectivity (le bouton devient disable) d'après https://www.robot-forum.com/robotforum/thread/26079-kuka-tcp-udp-communication/ , il faut l'option Ethernet/KRL dont la doc est: http://www.wtech.com.tw/public/download/manual/kuka/krc4/KST-Ethernet-KRL-21-En.pdf ===Export vers KRL==== Kuka sim pro 3.0.4 (programmation via interface graphique), export vers KRL langage du pendant Pour exporter en KRL, EXPORT->Generate Code exemple de projet exporté&: https://bvdp.inetdoc.net/files/iut/kuka/ ====Programme pour extraire les faces d'un rubiks cube==== #!/usr/bin/python3 #B. Vandeportaele 31/12/2019 import cv2 as cv import numpy as np #https://docs.opencv.org/master/db/d5b/tutorial_py_mouse_handling.html #pour afficher la liste des évenements possibles #events = [i for i in dir(cv) if 'EVENT' in i] #print( events ) #homography: # https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html?highlight=findhomography # cv2.findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask]]]) → retval, mask #You can use only inliers (i.e. points with corresponding mask value equals to 1) when doing your match. ''' #https://stackoverflow.com/questions/50945385/python-opencv-findhomography-inputs if len(good) > MIN_MATCH_COUNT: src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2) ''' #enregistrement et lecture fichiers #https://docs.scipy.org/doc/numpy/reference/generated/numpy.savetxt.html #https://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html if False: savePoints = np.float32((46, 82, 201, 30, 340, 85, 182, 144)).reshape(4,2) np.savetxt('test.out', savePoints, delimiter=',', fmt='%10.4f') filedata = np.genfromtxt('test.out', delimiter=',').astype('int32') print(filedata) exit() if False: #test pour estimer une homographie unité: srcPoints=np.float32( (1,2,1,4,5,9,7,8)).reshape(-1, 1, 2) dstPoints=srcPoints (retval, mask)= cv.findHomography(srcPoints, dstPoints) print("srcPoints") print(srcPoints) print("retval") print(retval) print("mask") print(mask) #rubikscube # pour l'apprentissage, donner les 4 coins de la face supérieure + 6 points pour apprendre les 6 couleurs de faces #éventuellement traiter les images en live pour pouvoir tourner le cube pendant l'apprentissage des couleurs image = cv.imread("rubiks.jpg", cv.IMREAD_UNCHANGED) if image is None: # ne pas utiliser == print("image non trouvee"); listeColors=["Blue","Red","White","Green","Orange","Yellow"] #listePositions=[(88,80),(188,125),(137,101),(198,44),(298,82),(192,84)] #apprentissage des couleurs sur la face top (pour le rouge) listePositions=[[88,80],[188,125],[137,101],[198,44],[298,82],[192,84]] #coordonée U puis V données par geeqie #apprentissage des couleurs sur la face front (pour le rouge) #listePositions=[[88,80],[210,243],[137,101],[198,44],[298,82],[192,84]] #coordonée U puis V données par geeqie listeRGB=[] print(listeColors) i=0 for color in listeColors: print(color) listeRGB.append( image[ listePositions[i][1],listePositions[i][0] ]) #coordonée V puis U i=i+1 print(listeRGB) #exit() #coordonnées obtenues avec geeqie,Affichage,infos sur le pixel #points dans le sens horaires for numface in range(0, 3): #trois faces pour débug #for numface in range(0, 1): #une seule face pour débug if numface==0: #https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html #srcPoints = np.float32((46,82,201,30,340,85,182,144)).reshape(-1, 1, 2) srcPoints = np.float32((46, 82, 201, 30, 340, 85, 182, 144)).reshape(4,2) elif numface==1: #srcPoints = np.float32((40,90,190,154,180,352,49,279)).reshape(-1, 1, 2) srcPoints = np.float32((40, 90, 190, 154, 180, 352, 49, 279)).reshape(4, 2) elif numface==2: #srcPoints = np.float32((186,152,347,92, 346,282,190,351)).reshape(-1, 1, 2) srcPoints = np.float32((186, 152, 347, 92, 346, 282, 190, 351)).reshape(4, 2) sizedest=30 dstPoints = np.float32((0,0,sizedest-1,0,sizedest-1,sizedest-1,0,sizedest-1)).reshape(-1, 1, 2) (H, mask) = cv.findHomography(srcPoints, dstPoints) print("srcPoints") print(srcPoints) print("retval") print(H) print("mask") print(mask) #fonction warpPespective: #https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html?highlight=warpperspective#void%20warpPerspective(InputArray%20src,%20OutputArray%20dst,%20InputArray%20M,%20Size%20dsize,%20int%20flags,%20int%20borderMode,%20const%20Scalar&%20borderValue dst=cv.warpPerspective(image, H, (sizedest,sizedest)) cv.imwrite('rubiksrect'+str(numface)+'.png', dst) #recherche pour chaque pixel la couleur la plus proche for u in range(0,sizedest): for v in range(0, sizedest): val=dst[v,u] listfdiff=[] for c in range(0,6): #pour chaque couleur à tester diff=0 for comp in range(0,3): #pour chacune des composantes de la couleur #print(listeRGB[c][comp]) #print(val[comp]) #print((listeRGB[c][comp]) - (val[comp])) # RuntimeWarning: overflow encountered in ubyte_scalars #print(abs( listeRGB[c][comp]-val[comp]) ) diff = diff+abs( int(listeRGB[c][comp])-int(val[comp])) #cast en int #print("diff:" +str(diff)) listfdiff.append(diff) #print("listeRGB[c]" + str(listeRGB[c])+ str(val)+" diff= "+str(diff)) l=listfdiff.index(min(listfdiff)) #indice du plus petit élément de la liste de différence #print(u,v,val,listfdiff,l) #if listfdiff[l]<150: dst[v,u]=listeRGB[l] #else: # dst[v, u] = [0,0,0] cv.imwrite('rubiksrect'+str(numface)+'_label.png', dst) #https: // docs.opencv.org / trunk / df / d9d / tutorial_py_colorspaces.html if False: # Convert BGR to HSV hsv = cv.cvtColor(dst, cv.COLOR_BGR2HSV) cv.imwrite('rubiksrect_hsv'+str(numface)+'.png', hsv) #en HSV pour calculer distance entre couleur il faut tenir compte du fait qu'il y a modulo 2Pi sur la teinte (codé modulo 180) ''' ####################################################### # mouse callback function def draw_circle(event,x,y,flags,param): if event == cv.EVENT_LBUTTONDBLCLK: cv.circle(img,(x,y),100,(255,0,0),-1) ####################################################### def nothing(x): pass ####################################################### if False: # Create a black image, a window and bind the function to window img = np.zeros((512,512,3), np.uint8) cv.namedWindow('image') cv.setMouseCallback('image',draw_circle) while(1): cv.imshow('image',img) if cv.waitKey(20) & 0xFF == 32 : # barre espace pour sortir ? 27: #escape? break cv.destroyAllWindows() else: drawing = False # true if mouse is pressed mode = True # if True, draw rectangle. Press 'm' to toggle to curve ix,iy = -1,-1 # mouse callback function def draw_circle2(event,x,y,flags,param): global ix,iy,drawing,mode # get current positions of four trackbars r = cv.getTrackbarPos('R', 'image') g = cv.getTrackbarPos('G', 'image') b = cv.getTrackbarPos('B', 'image') #s = cv.getTrackbarPos(switch, 'image') if event == cv.EVENT_LBUTTONDOWN: drawing = True ix,iy = x,y elif event == cv.EVENT_MOUSEMOVE: if drawing == True: if mode == True: #cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1) cv.rectangle(img, (ix, iy), (x, y), (r,g,b), -1) else: cv.circle(img,(x,y),5,(0,0,255),-1) elif event == cv.EVENT_LBUTTONUP: drawing = False if mode == True: #cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1) cv.rectangle(img, (ix, iy), (x, y), (r, g, b), -1) else: cv.circle(img,(x,y),5,(0,0,255),-1) cv.line(img, (ix,iy), (x, y), (255, 255, 0), 5) img = np.zeros((512,512,3), np.uint8) cv.namedWindow('image') # create trackbars for color change cv.createTrackbar('R', 'image', 0, 255, nothing) cv.createTrackbar('G', 'image', 0, 255, nothing) cv.createTrackbar('B', 'image', 0, 255, nothing) cv.setTrackbarPos('R', 'image',255) cv.setMouseCallback('image',draw_circle2) while(1): cv.imshow('image',img) k = cv.waitKey(1) & 0xFF if k == ord('m'): mode = not mode elif k == ord('s'): cv.imwrite('dessin.png', img) elif k == 27: #escape? break cv.destroyAllWindows() ''' ===Fichier image de rubiks cube=== {{https://bvdp.inetdoc.net/files/iut/kuka/rubiks.jpg}} ====Programme KRL==== &ACCESS RVP &REL 14 &PARAM EDITMASK = * &PARAM TEMPLATE = C:\KRC\Roboter\Template\vorgabe DEF TestTpCube( ) ;FOLD Declaration DECL INT cmdN DECL BOOL FLAG_END ;ENDFOLD (Declaration) ;FOLD INI;%{PE} ;FOLD BASISTECH INI GLOBAL INTERRUPT DECL 3 WHEN $STOPMESS==TRUE DO IR_STOPM ( ) INTERRUPT ON 3 BAS (#INITMOV,0 ) ;ENDFOLD (BASISTECH INI) ;FOLD USER INI ;Make your modifications here ;ENDFOLD (USER INI) ;ENDFOLD (INI) ;FOLD PTP HOME Vel= 100 % DEFAULT;%{PE}%MKUKATPBASIS,%CMOVE,%VPTP,%P 1:PTP, 2:HOME, 3:, 5:100, 7:DEFAULT $BWDSTART = FALSE PDAT_ACT=PDEFAULT FDAT_ACT=FHOME BAS (#PTP_PARAMS,100 ) $H_POS=XHOME PTP XHOME ;ENDFOLD ;FOLD initialisation et ouverture du canal ;Initialize and Open channel RET=EKI_Init("configcomcube") RET=EKI_Open("configcomcube") WAIT FOR $FLAG[1] FLAG_END = FALSE WHILE(NOT FLAG_END) ;Envoi des données OFFSET=0 FOR i=(1) TO (100) Cmd[i]=0 ENDFOR SWRITE(Cmd[],STATE,OFFSET,"%s","getmove") RET=EKI_Send("configcomcube",Cmd[]) wait sec 1.5 ;récupération des données ;RET = EKI_CheckBuffer("configcomcube","Buffer") ;wait sec 1 ;IF RET.Buff <> 0 THEN WAIT FOR $Out[60] RET=EKI_GetString("configcomcube","Buffer",Cmd[]) $Out[60]=FALSE wait sec 1 ;ENDIF ;************************************************************ OFFSET=0 SREAD(Cmd[],STATE,OFFSET,"%d",cmdN) if cmdN == 20 THEN ;msg = {modul[] "Info", nr 123, msg_txt[] "Caractere 20 recu"} ;opt = {vl_stop true, clear_p_reset true, clear_p_SAW false,log_to_DB true} ;nHandle = Set_KrlMsg (#notify, msg,par[], opt) ;wait sec 1 endif if cmdN == 0 THEN ;msg = {modul[] "Info", nr 123, msg_txt[] "Caractere 0 recu"} ;opt = {vl_stop true, clear_p_reset true, clear_p_SAW false,log_to_DB true} ;nHandle = Set_KrlMsg (#notify, msg,par[], opt) ;Fin de programme FLAG_END = TRUE ENDIF ENDWHILE ;FOLD PTP HOME Vel= 100 % DEFAULT;%{PE}%MKUKATPBASIS,%CMOVE,%VPTP,%P 1:PTP, 2:HOME, 3:, 5:100, 7:DEFAULT $BWDSTART = FALSE PDAT_ACT=PDEFAULT FDAT_ACT=FHOME BAS (#PTP_PARAMS,100 ) $H_POS=XHOME PTP XHOME ;ENDFOLD END FP: Fermeture Pince OP: Ouverture Pince Pi: Position i ^ FCW ^ FCCW ^ UCW ^ UCCW ^ MCW ^ MCCW ^ | OP | OP | OP | OP | OP | OP | | P3 | P2 | P5 | P6 | P5 | P6 | | P4 | P1 | P7 | P8 | P7 | P8 | | FP | FP | FP | FP | FP | FP | | P3 | P2 | P5 | P6 | P8 | P7 | | P2 | P3 | P6 | P5 | OP | OP | | P1 | P4 | P8 | P7 | P6 | P5 | | OP | OP | OP | OP | P0 | P0 | | P2 | P3 | P6 | P5 | | | | P0 | P0 | P0 | P0 | | | =====Adaptation Staubli===== modèle WRL: https://bvdp.inetdoc.net/files/iut/staubli/stl/rubikscube3d/ j'ai une erreur avec srs lors du chargement du wrl, même si je vire la texture... du coup je modélise un cube avec les lettres en 3D: https://bvdp.inetdoc.net/files/iut/staubli/stl/rubikscube3d/rubiks1.scad et https://bvdp.inetdoc.net/files/iut/staubli/stl/rubikscube3d/rubiks1.stl Configuration initiale: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_0_.png}} Configuration FCW: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_1_FCW.png}} Configuration FCCW: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_2_FCCW.png}} Configuration UCW: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_3_UCW.png}} Configuration UCCW: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_4_UCCW.png}} Configuration MCW: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_5_MCW.png}} Configuration MCCW: {{https://bvdp.inetdoc.net/files/iut/staubli/stl/rubiks_6_MCCW.png}}