[PYTHON] Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 3 Software

Was ist dieser Artikel?

Dieser Artikel ist serialisiert ** Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! Es ist Teil der Artikelsammlung mit dem Namen **. Klicken Sie hier für das ganze Bild

  1. Übersicht
  2. Algorithmus
  3. Software (dieser Artikel)
  4. Hardware

GitHub ist hier.

Das Werbevideo ist hier. Soltvvo

Die Videosammlung zu dieser Artikelsammlung ist hier. Lass uns einen Roboter bauen, der den Zauberwürfel löst!

Inhalt dieses Artikels

In diesem Artikel werde ich die Software von Soltvvo erklären, einem Roboter, der den von mir erstellten 2x2x2-Rubikwürfel löst.

Verfassung

Bevor die Software erklärt wird, muss notiert werden, welche Art von Konfiguration der Roboter ausführen wird. Es sieht aus wie die Figur. 構成.png

Hier ist Raspberry Pi ein Einplatinencomputer namens Raspberry Pi 4 (2 GB RAM) (dh ein gewöhnlicher (?) Computer), und ATMEGA328P ist ein berühmter Mikrocomputer (Mikrocontroller), der für eine Mikrocomputerplatine namens Arduino verwendet wird. Dieses Mal werde ich es als Arduino Uno verwenden. Es gibt zwei Schrittmotoren und zwei Servomotoren von ATMEGA328P. Dies ist der Aktuator (Kraft), der den Arm bewegt, der den Würfel dreht. Sie können auch die Webcam verwenden, um den Status des Cubes einzugeben.

Das ganze Bild

Ich werde Ihnen das ganze Bild des Programms zeigen.

Programmübersicht auf der Raspberry Pi-Seite

Programmübersicht auf der ATMEGA328P-Seite

Raspberry Pi Seitenprogramm-Python

Hier erklären wir das Programm auf der PC-Seite. Das Programm hat ungefähr 580 Zeilen und ist lang, daher werde ich es erklären, indem ich es in mehrere Teile teile.

Bibliothek importieren

Importieren Sie die Bibliothek, die Sie verwenden möchten.

from copy import deepcopy
from collections import deque
from time import time, sleep
import tkinter
import cv2
import numpy as np
import serial

Würfelklasse

Ich habe eine Cube-Klasse erstellt, weil es gut ist, sich einen Cube als eine "Sache" im Programm vorzustellen. Ich schäme mich zu sagen, dass dies meine erste Klasse in meinem Leben ist.

class Cube:
    def __init__(self):
        self.Co = [0, 0, 0, 0, 0, 0, 0]
        self.Cp = [0, 1, 2, 3, 4, 5, 6]

    #Rotationsverarbeitung CP
    def move_cp(self, num):
        surface = [[0, 1, 2, 3], [2, 3, 4, 5], [3, 1, 5, 6]]
        replace = [[2, 0, 3, 1], [3, 2, 1, 0], [1, 3, 0, 2]]
        idx = num // 3
        res = [i for i in self.Cp]
        for i, j in zip(surface[idx], replace[num % 3]):
            res[i] = self.Cp[surface[idx][j]]
        return res

    #Rotationsverarbeitung CO
    def move_co(self, num):
        surface = [[0, 1, 2, 3], [2, 3, 4, 5], [3, 1, 5, 6]]
        replace = [[2, 0, 3, 1], [3, 2, 1, 0], [1, 3, 0, 2]]
        pls = [2, 1, 1, 2]
        idx = num // 3
        res = [i for i in self.Co]
        for i, j in zip(surface[idx], replace[num % 3]):
            res[i] = self.Co[surface[idx][j]]
        if num // 3 != 0 and num % 3 != 1:
            for i in range(4):
                res[surface[idx][i]] += pls[i]
                res[surface[idx][i]] %= 3
        return res

    #Ändern Sie tatsächlich die Zustandsanordnung des Puzzles entsprechend der Rotationsnummer
    def move(self, num):
        res = Cube()
        res.Co = self.move_co(num)
        res.Cp = self.move_cp(num)
        return res

    #Erstellen Sie eine eindeutige Nummer aus dem cp-Array
    def cp2i(self):
        res = 0
        marked = set([])
        for i in range(7):
            res += fac[6 - i] * len(set(range(self.Cp[i])) - marked)
            marked.add(self.Cp[i])
        return res
    
    #Erstellen Sie eine eindeutige Nummer aus dem Co-Array
    def co2i(self):
        res = 0
        for i in self.Co:
            res *= 3
            res += i
        return res

Ich werde die Funktionen einzeln erklären. __init__(self)

    def __init__(self):
        self.Co = [0, 0, 0, 0, 0, 0, 0]
        self.Cp = [0, 1, 2, 3, 4, 5, 6]

Dies ist die erste Funktion, die ausgeführt wird, wenn die Cube-Klasse aufgerufen wird. Hier sind zwei Arrays, Co und Cp, der Cube-Klasse zugeordnet. Dies sind in der Reihenfolge CO (Abkürzung für Corner Orientation, die die Ausrichtung von Eckteilen angibt) und CP (Abkürzung für Corner Permutation, die die Position von Eckteilen angibt).

Darüber hinaus ist es sehr effizient, eine eigene CO- und CP-Sequenz zu haben, um den Zustand des Würfels aufrechtzuerhalten. Da sich nur R, U und F drehen müssen, bewegen sich die DLB-Teile (unten links hinten) nicht. Sie müssen die Informationen für diesen Teil nicht aufbewahren, Sie benötigen nur 7 Daten für jeden CO und CP.

CP nummeriert die Teile wie gezeigt und enthält die Werte, sodass das Teil an der Indexposition des Elements im CP-Array das Element im CP-Array ist. パーツ番号.png

Der CO ist 0, wenn der weiße oder gelbe Aufkleber auf der U- oder D-Seite erscheint, und wird jedes Mal 1 oder 2, wenn er von dort um 120 Grad im Uhrzeigersinn gedreht wird. Geben Sie dem Foto beispielsweise eine CO-Nummer. CO.png

move_cp(self, num)

    #Rotationsverarbeitung CP
    def move_cp(self, num):
        surface = [[0, 1, 2, 3], [2, 3, 4, 5], [3, 1, 5, 6]]
        replace = [[2, 0, 3, 1], [3, 2, 1, 0], [1, 3, 0, 2]]
        idx = num // 3
        res = [i for i in self.Cp]
        for i, j in zip(surface[idx], replace[num % 3]):
            res[i] = self.Cp[surface[idx][j]]
        return res

Diese Funktion ist (ein Teil von) der Funktion, die den Würfel tatsächlich dreht. Drehen Sie nur den CP des Arrays. Ich lege mich nicht mit CO an. Außerdem steht num für die Nummer des Rotationssymbols (Rotationsnummer) und entspricht dem Index des folgenden global platzierten Arrays.

move_candidate = ["U", "U2", "U'", "F", "F2", "F'", "R", "R2", "R'"] #Kandidat für die Rotation

Das Oberflächenarray zeigt die Teile, die sich bewegen, wenn die U-, F- und R-Ebenen verschoben werden, und das Ersetzungsarray zeigt, wie sich die Teile bewegen, wenn X, X2 bzw. X bewegt werden. Die Rotationsverarbeitung wird ermöglicht, indem diese Sequenzen gut ersetzt werden.

move_co(self, num)

    #Rotationsverarbeitung CO
    def move_co(self, num):
        surface = [[0, 1, 2, 3], [2, 3, 4, 5], [3, 1, 5, 6]]
        replace = [[2, 0, 3, 1], [3, 2, 1, 0], [1, 3, 0, 2]]
        pls = [2, 1, 1, 2]
        idx = num // 3
        res = [i for i in self.Co]
        for i, j in zip(surface[idx], replace[num % 3]):
            res[i] = self.Co[surface[idx][j]]
        if num // 3 != 0 and num % 3 != 1:
            for i in range(4):
                res[surface[idx][i]] += pls[i]
                res[surface[idx][i]] %= 3
        return res

Diese Funktion ist wie die obige Funktion move_cp eine Funktion, die das Teil tatsächlich verschiebt. Diese Funktion steuert CO. Der Inhalt ist der Funktion move_cp sehr ähnlich, aber der CO ändert sich, wenn die U-Ebene nicht gedreht wird (num% 3! = 0) und wenn sie nicht um 180 Grad gedreht wird (num% 3! = 1), sodass sie entsprechend verarbeitet werden muss. es gibt.

move(self, num)

    #Ändern Sie tatsächlich die Zustandsanordnung des Puzzles entsprechend der Rotationsnummer
    def move(self, num):
        res = Cube()
        res.Co = self.move_co(num)
        res.Cp = self.move_cp(num)
        return res

Diese Funktion ist auch eine Funktion, die das Teil tatsächlich bewegt. Diese Funktion steuert sowohl CO als auch CP. Ich führe nur die beiden oben genannten Funktionen aus.

cp2i(self)

    #Erstellen Sie eine eindeutige Nummer aus dem cp-Array
    def cp2i(self):
        res = 0
        marked = set([])
        for i in range(7):
            res += fac[6 - i] * len(set(range(self.Cp[i])) - marked)
            marked.add(self.Cp[i])
        return res

Diese Funktion generiert und gibt eine CP-Array-spezifische Nummer aus dem CP-Array zurück. Es wird nur die Sequenznummer des CP-Arrays zurückgegeben. Was ist eine Sequenznummer? 0: 0, 1, 2, 3, 4, 5, 6 1: 0, 1, 2, 3, 4, 6, 5 2: 0, 1, 2, 3, 5, 4, 6 3: 0, 1, 2, 3, 5, 6, 4 4: 0, 1, 2, 3, 6, 5, 4 Es ist die Nummer, wenn in der Reihenfolge wie angeordnet. Ich denke, die Website von hier ist leicht zu verstehen, wie diese Zahl berechnet wird.

co2i(self)

    #Erstellen Sie eine eindeutige Nummer aus dem Co-Array
    def co2i(self):
        res = 0
        for i in self.Co:
            res *= 3
            res += i
        return res

Diese Funktion generiert und gibt eine CO-Array-spezifische Nummer aus dem CO-Array zurück. Zu diesem Zeitpunkt wird eine eindeutige Nummer erstellt, indem das CO-Array als 7-stellige Nummer in ternären Nummern behandelt wird.

Funktionen im Zusammenhang mit anderen Rätseln

Es gibt eine Funktion namens num2moves, die Rotationszahlen in Rotationssymbole übersetzt.

#Rotationsnummer in Rotationssymbol konvertieren
def num2moves(arr):
    res = ''
    for i in arr:
        res += move_candidate[i] + ' '
    return res

move_candidate ist

move_candidate = ["U", "U2", "U'", "F", "F2", "F'", "R", "R2", "R'"] #Kandidat für die Rotation

ist.

Funktion zum Bewegen des Stellantriebs

Das ganze Bild ist hier.

def move_actuator(num, arg1, arg2, arg3=None):
    if arg3 == None:
        com = str(arg1) + ' ' + str(arg2)
    else:
        com = str(arg1) + ' ' + str(arg2) + ' ' + str(arg3)
    ser_motor[num].write((com + '\n').encode())
    ser_motor[num].flush()
    print('num:', num, 'command:', com)

def grab_p():
    for i in range(2):
        for j in range(2):
            move_actuator(i, j, 5)

def release_p():
    for i in range(2):
        for j in range(2):
            move_actuator(i, j, 6)

move_actuator(num, arg1, arg2, arg3=None)

#Senden Sie einen Befehl zum Bewegen des Stellantriebs
def move_actuator(num, arg1, arg2, arg3=None):
    if arg3 == None:
        com = str(arg1) + ' ' + str(arg2)
    else:
        com = str(arg1) + ' ' + str(arg2) + ' ' + str(arg3)
    ser_motor[num].write((com + '\n').encode())
    ser_motor[num].flush()
    print('num:', num, 'command:', com)

Diese Funktion sendet per serieller Kommunikation einen Befehl an ATMEGA328P, um den Motor zu bewegen. Hier ist num die ATMEGA328P-Nummer (0 oder 1), arg1 die Aktuatornummer, arg2 die Betätigungsmenge des Aktuators und arg3 die Drehzahl (U / min) des Motors beim Bewegen des Schrittmotors. ..

In Bezug auf die serielle Kommunikation,

ser_motor[0] = serial.Serial('/dev/ttyUSB0', 9600, write_timeout=0)
ser_motor[1] = serial.Serial('/dev/ttyUSB1', 9600, write_timeout=0)

Es gibt eine Definition. Jeder ATMEGA328P verfügt über zwei Schrittmotoren und zwei Servomotoren.

grab_p()

#Nimm den Würfel
def grab_p():
    for i in range(2):
        for j in range(2):
            move_actuator(i, j, 1000)

Diese Funktion verwendet die zuvor erwähnte Funktion move_actuator, um alle Arme gleichzeitig zu bewegen und den Würfel zu ergreifen.

release_p()

#Lassen Sie den Würfel los
def release_p():
    for i in range(2):
        for j in range(2):
            move_actuator(i, j, 2000)

Diese Funktion bewegt alle Arme gleichzeitig und gibt den Würfel frei.

GUI Der Teil, der sich auf die GUI bezieht, ist hier.

root = tkinter.Tk()
root.title("2x2x2solver")
root.geometry("300x150")

grid = 20
offset = 30

entry = [[None for _ in range(8)] for _ in range(6)]

for i in range(6):
    for j in range(8):
        if 1 < i < 4 or 1 < j < 4:
            entry[i][j] = tkinter.Entry(master=root, width=2, bg='gray')
            entry[i][j].place(x = j * grid + offset, y = i * grid + offset)

inspection = tkinter.Button(root, text="inspection", command=inspection_p)
inspection.place(x=0, y=0)

start = tkinter.Button(root, text="start", command=start_p)
start.place(x=0, y=40)


solutionvar = tkinter.StringVar(master=root, value='')
solution = tkinter.Label(textvariable=solutionvar)
solution.place(x=70, y=0)

solvingtimevar = tkinter.StringVar(master=root, value='')
solvingtime = tkinter.Label(textvariable=solvingtimevar)
solvingtime.place(x=120, y=20)

grab = tkinter.Button(root, text="grab", command=grab_p)
grab.place(x=0, y=120)

release = tkinter.Button(root, text="release", command=release_p)
release.place(x=120, y=120)

root.mainloop()

Ich benutze tkinter für die GUI-Bibliothek. Ich werde erklären, was jede Variable in der Abbildung entspricht. gui.png Obwohl hier nicht gezeigt, gibt es eine Beschriftung namens Lösungszeit, die auf dem Bildschirm anzeigt, wie lange es gedauert hat, den Würfel zu lösen. Der Grund, warum das Eingabefeld die Farbe anzeigt, besteht darin, dass dieses Programm ursprünglich den Status des Würfels durch Eingabe der Farbe in das Eingabefeld eingegeben hat. Eine solche Ära ist vorbei und wird jetzt von der Kamera erkannt.

Speichern Sie Puzzlefarben in einem Array

Das ganze Bild ist hier.

dic = {'w':'white', 'g':'green', 'r':'red', 'b':'blue', 'o':'magenta', 'y':'yellow'}
parts_place = [[[0, 2], [2, 0], [2, 7]], [[0, 3], [2, 6], [2, 5]], [[1, 2], [2, 2], [2, 1]], [[1, 3], [2, 4], [2, 3]], [[4, 2], [3, 1], [3, 2]], [[4, 3], [3, 3], [3, 4]], [[5, 3], [3, 5], [3, 6]], [[5, 2], [3, 7], [3, 0]]]
parts_color = [['w', 'o', 'b'], ['w', 'b', 'r'], ['w', 'g', 'o'], ['w', 'r', 'g'], ['y', 'o', 'g'], ['y', 'g', 'r'], ['y', 'r', 'b'], ['y', 'b', 'o']]

#Reflektieren Sie die Farbe in der Box
def confirm_p():
    global colors
    for i in range(6):
        for j in range(8):
            if (1 < i < 4 or 1 < j < 4) and colors[i][j] in j2color:
                entry[i][j]['bg'] = dic[colors[i][j]]
    #Füllen Sie die Stelle, an der die Farbe fixiert ist, wo sie nicht gefüllt ist
    for i in range(6):
        for j in range(8):
            if (1 < i < 4 or 1 < j < 4) and colors[i][j] == '':
                done = False
                for k in range(8):
                    if [i, j] in parts_place[k]:
                        for strt in range(3):
                            if parts_place[k][strt] == [i, j]:
                                idx = [colors[parts_place[k][l % 3][0]][parts_place[k][l % 3][1]] for l in range(strt + 1, strt + 3)]
                                for strt2 in range(3):
                                    idx1 = strt2
                                    idx2 = (strt2 + 1) % 3
                                    idx3 = (strt2 + 2) % 3
                                    for l in range(8):
                                        if parts_color[l][idx1] == idx[0] and parts_color[l][idx2] == idx[1]:
                                            colors[i][j] = parts_color[l][idx3]
                                            entry[i][j]['bg'] = dic[colors[i][j]]
                                            done = True
                                            break
                                    if done:
                                        break
                                break
                    if done:
                        break
    
    #Ändern Sie die Hintergrundfarbe des nicht ausgefüllten Bereichs in Grau
    for i in range(6):
        for j in range(8):
            if (1 < i < 4 or 1 < j < 4) and colors[i][j] == '':
                entry[i][j]['bg'] = 'gray'

Diese Funktion ist etwas kompliziert. Für 2x2x2 reicht es aus, nur die vier (gut ausgewählten) Seiten des Puzzles (z. B. die D-, F-, U- und B-Seiten in diesem Programm) zu betrachten, um den Status des Puzzles eindeutig zu bestimmen. Daher ist es diese Funktion, die die Farbe der Oberfläche errät und bestimmt, die nicht gesehen wird. Die festgelegte Farbe wird als Hintergrundfarbe des Eingabefelds angezeigt.

Diese Funktion führt einfach eine vollständige Suche durch, um herauszufinden, welche Farbe nicht gefüllt ist. Die Implementierung ist jedoch etwas schwer geworden. Ich bin mir nicht sicher, ob dies der beste Weg ist, es zu schreiben.

Holen Sie sich den Status des Puzzles von der Kamera

Das ganze Bild ist hier.

j2color = ['g', 'b', 'r', 'o', 'y', 'w']
idx = 0
colors = [['' for _ in range(8)] for _ in range(6)]

#Holen Sie sich den Stand des Puzzles
def detect():
    global idx, colors
    idx = 0
    while idx < 4:
        #color: g, b, r, o, y, w
        color_low = [[50, 50, 50],   [90, 150, 50],   [160, 150, 50], [170, 50, 50],  [20, 50, 50],   [0, 0, 50]] #for PC
        color_hgh = [[90, 255, 255], [140, 255, 255], [10, 255, 200], [20, 255, 255], [50, 255, 255], [179, 50, 255]]
        circlecolor = [(0, 255, 0), (255, 0, 0), (0, 0, 255), (0, 170, 255), (0, 255, 255), (255, 255, 255)]
        surfacenum = [[[4, 2], [4, 3], [5, 2], [5, 3]], [[2, 2], [2, 3], [3, 2], [3, 3]], [[0, 2], [0, 3], [1, 2], [1, 3]], [[3, 7], [3, 6], [2, 7], [2, 6]]]
        capture = cv2.VideoCapture(0)
        ret, frame = capture.read()
        capture.release()
        size_x = 200
        size_y = 150
        frame = cv2.resize(frame, (size_x, size_y))
        show_frame = deepcopy(frame)
        d = 50
        center = [size_x // 2, size_y // 2]
        tmp_colors = [['' for _ in range(8)] for _ in range(6)]
        hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
        dx = [-1, 1, -1, 1]
        dy = [1, 1, -1, -1]
        for i in range(4):
            y = center[0] + dy[i] * d
            x = center[1] + dx[i] * d
            cv2.circle(show_frame, (y, x), 5, (0, 0, 0), thickness=3, lineType=cv2.LINE_8, shift=0)
            val = hsv[x, y]
            for j in range(6):
                flag = True
                for k in range(3):
                    if not ((color_low[j][k] < color_hgh[j][k] and color_low[j][k] <= val[k] <= color_hgh[j][k]) or (color_low[j][k] > color_hgh[j][k] and (color_low[j][k] <= val[k] or val[k] <= color_hgh[j][k]))):
                        flag = False
                if flag:
                    tmp_colors[surfacenum[idx][i][0]][surfacenum[idx][i][1]] = j2color[j]
                    cv2.circle(show_frame, (y, x), 15, circlecolor[j], thickness=3, lineType=cv2.LINE_8, shift=0)
                    break
        cv2.imshow('title',show_frame)
        key = cv2.waitKey(0)
        if key == 32: #Wenn die Leertaste gedrückt wird
            for i in range(4):
                colors[surfacenum[idx][i][0]][surfacenum[idx][i][1]] = tmp_colors[surfacenum[idx][i][0]][surfacenum[idx][i][1]]
            print(idx)
            idx += 1
            confirm_p()
            if idx < 4:
                for i in range(2):
                    move_actuator(i, 0, 5)
                    move_actuator(i, 0, (-1) ** i, 50)
                    move_actuator(i, 1, 5)
                    move_actuator((i + 1) % 2, 1, 5)
                    move_actuator(i, 0, 6)
                    move_actuator(i, 0, -(-1) ** i, 50)
                    move_actuator(i, 0, 5)
                    move_actuator(i, 1, 6)
                    move_actuator((i + 1) % 2, 1, 6)
        cv2.destroyAllWindows()

Diese Funktion ermittelt die Farbe des Puzzles in Abhängigkeit von der Farbe der vier Pixel im von der Kamera aufgenommenen Bild. Der Bildschirm sieht so aus. カメラ.PNG

Erstellen Sie eine roboterspezifische Prozedur

Was mit IDA * in der später beschriebenen Inspektionsfunktion ausgegeben wird, ist eine Liste von Rotationssymbolen (Rotationsnummern) zum menschlichen Lesen. Wandeln Sie dies in ein Array mit Informationen darüber um, welcher Motor wann und wie oft bewegt werden soll. Das ganze Bild ist hier.

#Bestimmen Sie den zu drehenden Motor anhand der Anordnung der Drehsymbolnummern
def proc_motor(rot, num, direction):
    if num == len(ans):
        return rot, num, direction
    turn_arr = [-3, -2, -1]
    r_arr = [[-1, 2, 4, -1, 5, 1], [5, -1, 0, 2, -1, 3], [1, 3, -1, 4, 0, -1], [-1, 5, 1, -1, 2, 4], [2, -1, 3, 5, -1, 0], [4, 0, -1, 1, 3, -1]]
    f_arr = [[1, 2, 4, 5], [3, 2, 0, 5], [3, 4, 0, 1], [4, 2, 1, 5], [3, 5, 0, 2], [3, 1, 0, 4]]
    regrip_arr = [[21, 5, 9, 17, 20, 13, 10, 3, 4, 12, 18, 0, 23, 19, 11, 7, 8, 15, 22, 1, 16, 14, 6, 2], [4, 8, 16, 20, 12, 9, 2, 23, 15, 17, 3, 7, 18, 10, 6, 22, 14, 21, 0, 11, 13, 5, 1, 19]]
    regrip_rot = [[[1, -3], [3, -1]], [[0, -3], [2, -1]]]
    u_face = direction // 4
    f_face = f_arr[u_face][direction % 4]
    r_face = r_arr[u_face][f_face]
    d_face = (u_face + 3) % 6
    b_face = (f_face + 3) % 6
    l_face = (r_face + 3) % 6
    move_able = [r_face, b_face, l_face, f_face]
    move_face = ans[num] // 3
    move_amount = turn_arr[ans[num] % 3]
    if move_face == u_face or move_face == d_face:
        rot_tmp = [[i for i in rot] for _ in range(2)]
        direction_tmp = [-1, -1]
        num_tmp = [num, num]
        for j in range(2):
            rot_tmp[j].extend(regrip_rot[j])
            direction_tmp[j] = regrip_arr[j][direction]
            rot_tmp[j], num_tmp[j], direction_tmp[j] = proc_motor(rot_tmp[j], num_tmp[j], direction_tmp[j])
        idx = 0 if len(rot_tmp[0]) < len(rot_tmp[1]) else 1
        rot_res = rot_tmp[idx]
        num_res = num_tmp[idx]
        direction_res = direction_tmp[idx]
    else:
        tmp = move_able.index(move_face)
        rot_res = [i for i in rot]
        rot_res.append([tmp, move_amount])
        rot_res, num_res, direction_res = proc_motor(rot_res, num + 1, direction)
    return rot_res, num_res, direction_res

#Optimierung der Roboterprozedur
def rot_optimise():
    global rot
    i = 0
    tmp_arr = [0, -3, -2, -1]
    while i < len(rot):
        if i < len(rot) - 1 and rot[i][0] == rot[i + 1][0]:
            tmp = tmp_arr[(rot[i][1] + rot[i + 1][1]) % 4]
            del rot[i + 1]
            if not tmp:
                del rot[i]
                i -= 1
            else:
                rot[i][1] = tmp
        elif i < len(rot) - 2 and rot[i][0] == rot[i + 2][0] and rot[i][0] % 2 == rot[i + 1][0] % 2:
            tmp = tmp_arr[rot[i][1] + rot[i + 2][1] + 2]
            del rot[i + 2]
            if tmp == 0:
                del rot[i]
                i -= 1
            else:
                rot[i][1] = tmp
        i += 1

Es hört sich so an, als würdest du etwas sehr Dummes tun. Es gibt eine Reihe von lächerlich langen handgemachten Arrays. Lassen Sie uns die Funktionen einzeln erklären.

proc_motor(rot, num, direction)

#Bestimmen Sie den zu drehenden Motor anhand der Anordnung der Drehsymbolnummern
def proc_motor(rot, num, direction):
    if num == len(ans):
        return rot, num, direction
    turn_arr = [-3, -2, -1]
    r_arr = [[-1, 2, 4, -1, 5, 1], [5, -1, 0, 2, -1, 3], [1, 3, -1, 4, 0, -1], [-1, 5, 1, -1, 2, 4], [2, -1, 3, 5, -1, 0], [4, 0, -1, 1, 3, -1]]
    f_arr = [[1, 2, 4, 5], [3, 2, 0, 5], [3, 4, 0, 1], [4, 2, 1, 5], [3, 5, 0, 2], [3, 1, 0, 4]]
    regrip_arr = [[21, 5, 9, 17, 20, 13, 10, 3, 4, 12, 18, 0, 23, 19, 11, 7, 8, 15, 22, 1, 16, 14, 6, 2], [4, 8, 16, 20, 12, 9, 2, 23, 15, 17, 3, 7, 18, 10, 6, 22, 14, 21, 0, 11, 13, 5, 1, 19]]
    regrip_rot = [[[1, -3], [3, -1]], [[0, -3], [2, -1]]]
    u_face = direction // 4
    f_face = f_arr[u_face][direction % 4]
    r_face = r_arr[u_face][f_face]
    d_face = (u_face + 3) % 6
    b_face = (f_face + 3) % 6
    l_face = (r_face + 3) % 6
    move_able = [r_face, b_face, l_face, f_face]
    move_face = ans[num] // 3
    move_amount = turn_arr[ans[num] % 3]
    if move_face == u_face or move_face == d_face:
        rot_tmp = [[i for i in rot] for _ in range(2)]
        direction_tmp = [-1, -1]
        num_tmp = [num, num]
        for j in range(2):
            rot_tmp[j].extend(regrip_rot[j])
            direction_tmp[j] = regrip_arr[j][direction]
            rot_tmp[j], num_tmp[j], direction_tmp[j] = proc_motor(rot_tmp[j], num_tmp[j], direction_tmp[j])
        idx = 0 if len(rot_tmp[0]) < len(rot_tmp[1]) else 1
        rot_res = rot_tmp[idx]
        num_res = num_tmp[idx]
        direction_res = direction_tmp[idx]
    else:
        tmp = move_able.index(move_face)
        rot_res = [i for i in rot]
        rot_res.append([tmp, move_amount])
        rot_res, num_res, direction_res = proc_motor(rot_res, num + 1, direction)
    return rot_res, num_res, direction_res

Ich werde über die Details im Hardware-Abschnitt sprechen, aber der Roboter hat vier Arme, die an den Seiten F, R, B und L angebracht sind. Mit anderen Worten, die U-Seite kann nur gedreht werden, wenn Sie den Besitzer wechseln. Hier ist das Schalten wie das gleichzeitige Drehen der R-Seite und der L-Seite. Es gibt $ 4 (Straßen / Gesichter) \ times6 (Gesichter) = 24 Möglichkeiten, den Würfel zu halten. Weisen Sie also jeder Richtung eine Nummer zu und wie Sie von jeder Richtung in welche Richtung wechseln. Im Array Regrip_arr (das ist harte Arbeit). Es gibt zwei Möglichkeiten, gleichzeitig zwischen RL und FB zu wechseln (ob im oder gegen den Uhrzeigersinn, die Werte sind immerhin gleich).

Ausgehend von der aktuellen Würfelausrichtung verwenden wir die rekursive vollständige Suche, um herauszufinden, ob der nächste Schritt ohne Umschalten gedreht werden kann und welche der beiden Umschaltungen bei Bedarf effizienter ist.

Die Werte werden wie unten gezeigt im rot-Array gespeichert.

rot = [[Anzahl der zu drehenden Motoren,Richtung und Ausmaß der Drehung], [0, -90], [1, -180], [2, -270]] #Motor 0 um 90 Grad gegen den Uhrzeigersinn drehen, Motor 2 um 180 Grad, Motor 2 um 270 Grad gegen den Uhrzeigersinn(=90 Grad im Uhrzeigersinn)Sich wenden an

Es gibt einen hardwareabhängigen Grund, es hier nur gegen den Uhrzeigersinn zu verwenden. Einzelheiten finden Sie im Abschnitt Hardware.

rot_optimise()

#Optimierung der Roboterprozedur
def rot_optimise():
    global rot
    i = 0
    tmp_arr = [0, -3, -2, -1]
    while i < len(rot):
        if i < len(rot) - 1 and rot[i][0] == rot[i + 1][0]:
            tmp = tmp_arr[(rot[i][1] + rot[i + 1][1]) % 4]
            del rot[i + 1]
            if not tmp:
                del rot[i]
                i -= 1
            else:
                rot[i][1] = tmp
        elif i < len(rot) - 2 and rot[i][0] == rot[i + 2][0] and rot[i][0] % 2 == rot[i + 1][0] % 2:
            tmp = tmp_arr[rot[i][1] + rot[i + 2][1] + 2]
            del rot[i + 2]
            if tmp == 0:
                del rot[i]
                i -= 1
            else:
                rot[i][1] = tmp
        i += 1

Die erzeugte roboterspezifische Prozedur umfasst beispielsweise ein Rot-Array.

rot = [[0, -90], [0, -90], [2, -90]]

Es kann wie folgt vereinfacht werden. Das Rotationssymbol ist in diesem Fall F'F'R '. Dies kann zu F2 R 'vereinfacht werden.

rot = [[0, -180], [2, -90]]

Wir machen diese Vereinfachung innerhalb dieser Funktion.

Inspektionsprozess

Nun der Hauptprozess (der Prozess der Suche nach einer Lösung für das Rätsel).

#Inspektionsprozess
def inspection_p():
    global ans, rot, colors

    ans = []
    rot = []
    colors = [['' for _ in range(8)] for _ in range(6)]

    grab_p()
    for i in range(2):
        move_actuator(i, 1, 2000)
    detect()

    strt = time()
    
    #Erstellen Sie ein Puzzle-Status-Array aus Farbinformationen
    confirm_p()
    puzzle = Cube()
    set_parts_color = [set(i) for i in parts_color]
    for i in range(7):
        tmp = []
        for j in range(3):
            tmp.append(colors[parts_place[i][j][0]][parts_place[i][j][1]])
        tmp1 = 'w' if 'w' in tmp else 'y'
        puzzle.Co[i] = tmp.index(tmp1)
        if not set(tmp) in set_parts_color:
            solutionvar.set('cannot solve!')
            print('cannot solve!')
            return
        puzzle.Cp[i] = set_parts_color.index(set(tmp))
    tmp2 = list(set(range(7)) - set(puzzle.Cp))
    if len(tmp2):
        tmp2 = tmp2[0]
        for i in range(7):
            if puzzle.Cp[i] > tmp2:
                puzzle.Cp[i] -= 1
    print('scramble:')
    for i in range(6):
        print(colors[i])
    print(puzzle.Cp)
    print(puzzle.Co)

    #Erstellen Sie aus der Ausrichtung des Puzzles ein Array gelöster Zustände
    solved_color = [['' for _ in range(8)] for _ in range(6)]
    solved_color[5][2] = colors[5][2]
    solved_color[3][7] = colors[3][7]
    solved_color[3][0] = colors[3][0]
    solved_color[2][2] = j2color[(j2color.index(solved_color[3][7]) // 2) * 2 - j2color.index(solved_color[3][7]) % 2 + 1]
    solved_color[3][4] = j2color[(j2color.index(solved_color[3][0]) // 2) * 2 - j2color.index(solved_color[3][0]) % 2 + 1]
    solved_color[0][2] = j2color[(j2color.index(solved_color[5][2]) // 2) * 2 - j2color.index(solved_color[5][2]) % 2 + 1]
    for i in range(6):
        for j in range(8):
            if (1 < i < 4 or 1 < j < 4) and solved_color[i][j] == '':
                if i % 2 and j % 2:
                    dx = [0, -1, -1]
                    dy = [-1, -1, 0]
                elif i % 2 and (not j % 2):
                    dx = [0, 1, 1]
                    dy = [-1, -1, 0]
                elif (not i % 2) and j % 2:
                    dx = [-1, -1, 0]
                    dy = [0, 1, 1]
                elif (not i % 2) and (not j % 2):
                    dx = [1, 1, 0]
                    dy = [0, 1, 1]
                #print(i, j, dx, dy)
                for k in range(3):
                    if solved_color[i + dy[k]][j + dx[k]] != '':
                        solved_color[i][j] = solved_color[i + dy[k]][j + dx[k]]
    solved = Cube()
    for i in range(7):
        tmp = []
        for j in range(3):
            tmp.append(solved_color[parts_place[i][j][0]][parts_place[i][j][1]])
        tmp1 = 'w' if 'w' in tmp else 'y'
        solved.Co[i] = tmp.index(tmp1)
        solved.Cp[i] = set_parts_color.index(set(tmp))
    tmp2 = list(set(range(7)) - set(solved.Cp))
    if len(tmp2):
        tmp2 = tmp2[0]
        for i in range(7):
            if solved.Cp[i] > tmp2:
                solved.Cp[i] -= 1
    print('solved:')
    for i in range(6):
        print(solved_color[i])
    print(solved.Cp)
    print(solved.Co)

    #Co- und cp-Sequenzen zum Beschneiden
    direction = -1
    direction_arr = [21, 12, 15, 18, 2, 22, 20, 4, 8, 13, 23, 1, 6, 0, 3, 9, 11, 16, 14, 7, 5, 19, 17, 10]
    for idx, d in enumerate(direction_arr):
        if solved_color[5][2] == parts_color[d // 3][d % 3] and solved_color[3][7] == parts_color[d // 3][(d % 3 + 1) % 3]:
            direction = idx
    if direction == -1:
        solutionvar.set('cannot solve!')
        print('cannot solve!')
        return
    with open('cp'+ str(direction) + '.csv', mode='r') as f:
        cp = [int(i) for i in f.readline().replace('\n', '').split(',')]
    with open('co'+ str(direction) + '.csv', mode='r') as f:
        co = [int(i) for i in f.readline().replace('\n', '').split(',')]
    print('pre', time() - strt, 's')

    #Tiefenprioritätssuche mit Schnitt
    def dfs(status, depth, num):
        global ans
        if num + max(cp[status.cp2i()], co[status.co2i()]) <= depth:
            l_mov = ans[-1] if num else -1
            t = (l_mov // 3) * 3
            lst = set(range(9)) - set([t, t + 1, t + 2])
            for mov in lst:
                n_status = status.move(mov)
                ans.append(mov)
                if num + 1 == depth and n_status.Cp == solved.Cp and n_status.Co == solved.Co:
                    return True
                if dfs(n_status, depth, num + 1):
                    return True
                ans.pop()
        return False

    # IDA*
    for depth in range(1, 12):
        ans = []
        if dfs(puzzle, depth, 0):
            break
    
    if ans:
        print('answer:', num2moves(ans))
        solutionvar.set(num2moves(ans))
        rot, _, _ = proc_motor(rot, 0, 4)
        print('before:', len(rot))
        print(rot)
        rot_optimise()
        print('after:', len(rot))
        print(rot)
        print('all', time() - strt, 's')
    else:
        solutionvar.set('cannot solve!')
        print('cannot solve!')

Es ist lang. Warum hast du es nicht zu einer Funktion gemacht? Ich werde jeden Block erklären.

Erstellen Sie ein Puzzle-Status-Array aus Farbinformationen

    #Erstellen Sie ein Puzzle-Status-Array aus Farbinformationen
    confirm_p()
    puzzle = Cube()
    set_parts_color = [set(i) for i in parts_color]
    for i in range(7):
        tmp = []
        for j in range(3):
            tmp.append(colors[parts_place[i][j][0]][parts_place[i][j][1]])
        tmp1 = 'w' if 'w' in tmp else 'y'
        puzzle.Co[i] = tmp.index(tmp1)
        if not set(tmp) in set_parts_color:
            solutionvar.set('cannot solve!')
            print('cannot solve!')
            return
        puzzle.Cp[i] = set_parts_color.index(set(tmp))
    tmp2 = list(set(range(7)) - set(puzzle.Cp))
    if len(tmp2):
        tmp2 = tmp2[0]
        for i in range(7):
            if puzzle.Cp[i] > tmp2:
                puzzle.Cp[i] -= 1
    print('scramble:')
    for i in range(6):
        print(colors[i])
    print(puzzle.Cp)
    print(puzzle.Co)

In diesem Teil werden aus den Farben des Farbarrays ein CO-Array (puzzle.Co) und ein CP-Array (puzzle.Cp) generiert. Ich suche nur ehrlich, aber die Implementierung ist etwas schwer. parts_color und parts_place befinden sich im folgenden Array.

parts_place = [[[0, 2], [2, 0], [2, 7]], [[0, 3], [2, 6], [2, 5]], [[1, 2], [2, 2], [2, 1]], [[1, 3], [2, 4], [2, 3]], [[4, 2], [3, 1], [3, 2]], [[4, 3], [3, 3], [3, 4]], [[5, 3], [3, 5], [3, 6]], [[5, 2], [3, 7], [3, 0]]]
parts_color = [['w', 'o', 'b'], ['w', 'b', 'r'], ['w', 'g', 'o'], ['w', 'r', 'g'], ['y', 'o', 'g'], ['y', 'g', 'r'], ['y', 'r', 'b'], ['y', 'b', 'o']]

Erstellen Sie aus der Ausrichtung des Puzzles ein Array gelöster Zustände

    #Erstellen Sie aus der Ausrichtung des Puzzles ein Array gelöster Zustände
    solved_color = [['' for _ in range(8)] for _ in range(6)]
    solved_color[5][2] = colors[5][2]
    solved_color[3][7] = colors[3][7]
    solved_color[3][0] = colors[3][0]
    solved_color[2][2] = j2color[(j2color.index(solved_color[3][7]) // 2) * 2 - j2color.index(solved_color[3][7]) % 2 + 1]
    solved_color[3][4] = j2color[(j2color.index(solved_color[3][0]) // 2) * 2 - j2color.index(solved_color[3][0]) % 2 + 1]
    solved_color[0][2] = j2color[(j2color.index(solved_color[5][2]) // 2) * 2 - j2color.index(solved_color[5][2]) % 2 + 1]
    for i in range(6):
        for j in range(8):
            if (1 < i < 4 or 1 < j < 4) and solved_color[i][j] == '':
                if i % 2 and j % 2:
                    dx = [0, -1, -1]
                    dy = [-1, -1, 0]
                elif i % 2 and (not j % 2):
                    dx = [0, 1, 1]
                    dy = [-1, -1, 0]
                elif (not i % 2) and j % 2:
                    dx = [-1, -1, 0]
                    dy = [0, 1, 1]
                elif (not i % 2) and (not j % 2):
                    dx = [1, 1, 0]
                    dy = [0, 1, 1]
                #print(i, j, dx, dy)
                for k in range(3):
                    if solved_color[i + dy[k]][j + dx[k]] != '':
                        solved_color[i][j] = solved_color[i + dy[k]][j + dx[k]]
    solved = Cube()
    for i in range(7):
        tmp = []
        for j in range(3):
            tmp.append(solved_color[parts_place[i][j][0]][parts_place[i][j][1]])
        tmp1 = 'w' if 'w' in tmp else 'y'
        solved.Co[i] = tmp.index(tmp1)
        solved.Cp[i] = set_parts_color.index(set(tmp))
    tmp2 = list(set(range(7)) - set(solved.Cp))
    if len(tmp2):
        tmp2 = tmp2[0]
        for i in range(7):
            if solved.Cp[i] > tmp2:
                solved.Cp[i] -= 1
    print('solved:')
    for i in range(6):
        print(solved_color[i])
    print(solved.Cp)
    print(solved.Co)

Angenommen, die Ausrichtung des Puzzles ist keine feste Ausrichtung, sondern die Farben werden zufällig eingegeben. Um diesen Fall gut zu behandeln, erstellen Sie zunächst ein Array "olved_color ", das den Farbstatus des Puzzles im gelösten Status enthält. Wo ist j2color?

j2color = ['g', 'b', 'r', 'o', 'y', 'w']

Dann werden wir tatsächlich gelöst erstellen (Cube-Klasse). Da die Ausrichtung des Puzzles zufällig ist, kann das gelöste Array nicht eindeutig bestimmt werden.

Suche nach IDA *

#Co- und cp-Sequenzen zum Beschneiden
    direction = -1
    direction_arr = [21, 12, 15, 18, 2, 22, 20, 4, 8, 13, 23, 1, 6, 0, 3, 9, 11, 16, 14, 7, 5, 19, 17, 10]
    for idx, d in enumerate(direction_arr):
        if solved_color[5][2] == parts_color[d // 3][d % 3] and solved_color[3][7] == parts_color[d // 3][(d % 3 + 1) % 3]:
            direction = idx
    if direction == -1:
        solutionvar.set('cannot solve!')
        print('cannot solve!')
        return
    with open('cp'+ str(direction) + '.csv', mode='r') as f:
        cp = [int(i) for i in f.readline().replace('\n', '').split(',')]
    with open('co'+ str(direction) + '.csv', mode='r') as f:
        co = [int(i) for i in f.readline().replace('\n', '').split(',')]
    print('pre', time() - strt, 's')

    #Tiefenprioritätssuche mit Schnitt
    def dfs(status, depth, num):
        global ans
        if num + max(cp[status.cp2i()], co[status.co2i()]) <= depth:
            l_mov = ans[-1] if num else -1
            t = (l_mov // 3) * 3
            lst = set(range(9)) - set([t, t + 1, t + 2])
            for mov in lst:
                n_status = status.move(mov)
                ans.append(mov)
                if num + 1 == depth and n_status.Cp == solved.Cp and n_status.Co == solved.Co:
                    return True
                if dfs(n_status, depth, num + 1):
                    return True
                ans.pop()
        return False

    # IDA*
    for depth in range(1, 12):
        ans = []
        if dfs(puzzle, depth, 0):
            break

Hier ist die Suche, die schließlich in der Algorithmus-Edition veröffentlicht wurde. Es unterscheidet sich jedoch stark vom Algorithmus-Versionsprogramm. Im Algorithmusabschnitt wurde die Suche nach Tiefenpriorität mit einem Stapel anstatt mit einem Rekursiv geschrieben, um die Lesbarkeit zu betonen. Hier wird jedoch mit Rekursiv die Speichernutzung erfolgreich auf das Äußerste reduziert. Da es nur 24 Variationen von Bereinigungssequenzen gibt (2 Sätze CP und CO), berechnet das folgende Programm eine CSV-Datei vor und erstellt sie.

import csv
from collections import deque

class Cube:
    def __init__(self):
        self.Co = [0, 0, 0, 0, 0, 0, 0]
        self.Cp = [0, 1, 2, 3, 4, 5, 6]
        self.Moves = []
        #self.Movnum = 0

    #Rotationsverarbeitung CP
    def move_cp(self, num):
        surface = [[0, 1, 2, 3], [2, 3, 4, 5], [3, 1, 5, 6]]
        replace = [[2, 0, 3, 1], [3, 2, 1, 0], [1, 3, 0, 2]]
        idx = num // 3
        res = Cube()
        res.Cp = [i for i in self.Cp]
        for i, j in zip(surface[idx], replace[num % 3]):
            res.Cp[i] = self.Cp[surface[idx][j]]
        res.Moves = [i for i in self.Moves]
        res.Moves.append(num)
        return res

    #Rotationsverarbeitung CO
    def move_co(self, num):
        surface = [[0, 1, 2, 3], [2, 3, 4, 5], [3, 1, 5, 6]]
        replace = [[2, 0, 3, 1], [3, 2, 1, 0], [1, 3, 0, 2]]
        pls = [2, 1, 1, 2]
        idx = num // 3
        res = Cube()
        res.Co = [i for i in self.Co]
        for i, j in zip(surface[idx], replace[num % 3]):
            res.Co[i] = self.Co[surface[idx][j]]
        if num // 3 != 0 and num % 3 != 1:
            for i in range(4):
                res.Co[surface[idx][i]] += pls[i]
                res.Co[surface[idx][i]] %= 3
        res.Moves = [i for i in self.Moves]
        res.Moves.append(num)
        return res

    #Ändern Sie tatsächlich die Zustandsanordnung des Puzzles entsprechend der Rotationsnummer
    def move(self, num):
        res = Cube()
        res = self.move_co(num)
        res.Cp = self.move_cp(num).Cp
        return res

    #Erstellen Sie eine eindeutige Nummer aus dem cp-Array
    def cp2i(self):
        res = 0
        marked = set([])
        for i in range(7):
            res += fac[6 - i] * len(set(range(self.Cp[i])) - marked)
            marked.add(self.Cp[i])
        return res
    
    #Erstellen Sie eine eindeutige Nummer aus dem Co-Array
    def co2i(self):
        res = 0
        for i in self.Co:
            res *= 3
            res += i
        return res

parts_place = [[[0, 2], [2, 0], [2, 7]], [[0, 3], [2, 6], [2, 5]], [[1, 2], [2, 2], [2, 1]], [[1, 3], [2, 4], [2, 3]], [[4, 2], [3, 1], [3, 2]], [[4, 3], [3, 3], [3, 4]], [[5, 3], [3, 5], [3, 6]], [[5, 2], [3, 7], [3, 0]]]
parts_color = [['w', 'o', 'b'], ['w', 'b', 'r'], ['w', 'g', 'o'], ['w', 'r', 'g'], ['y', 'o', 'g'], ['y', 'g', 'r'], ['y', 'r', 'b'], ['y', 'b', 'o']]
j2color = ['g', 'b', 'r', 'o', 'y', 'w']
direction_arr = [21, 12, 15, 18, 2, 22, 20, 4, 8, 13, 23, 1, 6, 0, 3, 9, 11, 16, 14, 7, 5, 19, 17, 10]

fac = [1]
for i in range(1, 8):
    fac.append(fac[-1] * i)

for idx, d in enumerate(direction_arr):
    set_parts_color = [set(i) for i in parts_color]
    solved_color = [['' for _ in range(8)] for _ in range(6)]
    solved_color[5][2] = parts_color[d // 3][d % 3]
    solved_color[3][7] = parts_color[d // 3][(d % 3 + 1) % 3]
    solved_color[3][0] = parts_color[d // 3][(d % 3 + 2) % 3]
    solved_color[2][2] = j2color[(j2color.index(solved_color[3][7]) // 2) * 2 - j2color.index(solved_color[3][7]) % 2 + 1]
    solved_color[3][4] = j2color[(j2color.index(solved_color[3][0]) // 2) * 2 - j2color.index(solved_color[3][0]) % 2 + 1]
    solved_color[0][2] = j2color[(j2color.index(solved_color[5][2]) // 2) * 2 - j2color.index(solved_color[5][2]) % 2 + 1]
    for i in range(6):
        for j in range(8):
            if (1 < i < 4 or 1 < j < 4) and solved_color[i][j] == '':
                if i % 2 and j % 2:
                    dx = [0, -1, -1]
                    dy = [-1, -1, 0]
                elif i % 2 and (not j % 2):
                    dx = [0, 1, 1]
                    dy = [-1, -1, 0]
                elif (not i % 2) and j % 2:
                    dx = [-1, -1, 0]
                    dy = [0, 1, 1]
                elif (not i % 2) and (not j % 2):
                    dx = [1, 1, 0]
                    dy = [0, 1, 1]
                #print(i, j, dx, dy)
                for k in range(3):
                    if solved_color[i + dy[k]][j + dx[k]] != '':
                        solved_color[i][j] = solved_color[i + dy[k]][j + dx[k]]
    solved = Cube()
    for i in range(7):
        tmp = []
        for j in range(3):
            tmp.append(solved_color[parts_place[i][j][0]][parts_place[i][j][1]])
        tmp1 = 'w' if 'w' in tmp else 'y'
        solved.Co[i] = tmp.index(tmp1)
        solved.Cp[i] = set_parts_color.index(set(tmp))
    tmp2 = list(set(range(7)) - set(solved.Cp))
    if len(tmp2):
        tmp2 = tmp2[0]
        for i in range(7):
            if solved.Cp[i] > tmp2:
                solved.Cp[i] -= 1
    print('solved:')
    for i in range(6):
        print(solved_color[i])
    print(solved.Cp)
    print(solved.Co)
    print(idx)

    #Co- und cp-Sequenzen zum Beschneiden
    inf = 100
    cp = [inf for _ in range(fac[7])]
    cp_solved = Cube()
    cp_solved.Cp = solved.Cp
    cp[cp_solved.cp2i()] = 0
    que = deque([cp_solved])
    while len(que):
        status = que.popleft()
        num = len(status.Moves)
        l_mov = status.Moves[-1] if num else -1
        t = (l_mov // 3) * 3
        lst = set(range(9)) - set([t, t + 1, t + 2])
        for mov in lst:
            n_status = status.move_cp(mov)
            n_idx = n_status.cp2i()
            if cp[n_idx] == inf:
                cp[n_idx] = len(n_status.Moves) #n_status.Movnum
                que.append(n_status)
    co = [inf for _ in range(3 ** 7)]
    co_solved = Cube()
    co_solved.Co = solved.Co
    co[co_solved.co2i()] = 0
    que = deque([co_solved])
    while len(que):
        status = que.popleft()
        num = len(status.Moves)
        l_mov = status.Moves[-1] if num else -1
        t = (l_mov // 3) * 3
        lst = set(range(9)) - set([t, t + 1, t + 2])
        for mov in lst:
            n_status = status.move_co(mov)
            n_idx = n_status.co2i()
            if co[n_idx] == inf:
                co[n_idx] = len(n_status.Moves) #n_status.Movnum
                que.append(n_status)
    
    with open('cp' + str(idx) + '.csv', mode='x') as f:
            writer = csv.writer(f, lineterminator='\n')
            writer.writerow(cp)
    with open('co' + str(idx) + '.csv', mode='x') as f:
            writer = csv.writer(f, lineterminator='\n')
            writer.writerow(co)

Da sich die Klasse ein wenig von der zuvor erläuterten unterscheidet, ist das zu veröffentlichende Programm lang geworden, aber es ist keine große Sache. Ehrlich gesagt ist die kürzeste Anzahl von Schritten, die erforderlich sind, um entweder CO oder CP für 24 Haltemöglichkeiten auszurichten, in csv zusammengefasst.

Generieren Sie ein Array, um den Roboter zu bewegen

    if ans:
        print('answer:', num2moves(ans))
        solutionvar.set(num2moves(ans))
        rot, _, _ = proc_motor(rot, 0, 4)
        print('before:', len(rot))
        print(rot)
        rot_optimise()
        print('after:', len(rot))
        print(rot)
        print('all', time() - strt, 's')
    else:
        solutionvar.set('cannot solve!')
        print('cannot solve!')

Hier wird die Antwort als vom Menschen gelesenes Rotationssymbol in ein Array zum Bewegen des Roboters umgewandelt. Außerdem wird ein Rotationssymbol auf dem Bildschirm angezeigt. Wenn die Würfel nicht ausgerichtet sind, kann nicht gespalten werden! Wird angezeigt.

Bewegen Sie den Roboter tatsächlich

Hier wird der Roboter tatsächlich durch Kommunikation mit dem ATMEGA328P bewegt.

#Bewegen Sie den Roboter tatsächlich
def start_p():
    print('start!')
    strt_solv = time()
    i = 0
    while i < len(rot):
        if GPIO.input(4) == GPIO.LOW:
            solvingtimevar.set('emergency stop')
            print('emergency stop')
            return
        grab = rot[i][0] % 2
        for j in range(2):
            move_actuator(j, grab, 1000)
        sleep(0.4)
        for j in range(2):
            move_actuator(j, (grab + 1) % 2, 2000)
        sleep(0.1)
        ser_num = rot[i][0] // 2
        rpm = 100
        offset = -5
        move_actuator(ser_num, rot[i][0] % 2, rot[i][1] * 90 + offset, rpm)
        max_turn = abs(rot[i][1])
        flag = i < len(rot) - 1 and rot[i + 1][0] % 2 == rot[i][0] % 2
        if flag:
            move_actuator(rot[i + 1][0] // 2, rot[i + 1][0] % 2, rot[i + 1][1] * 90 + offset, rpm)
            max_turn = max(max_turn, abs(rot[i + 1][1]))
        slptim = 60 / rpm * (max_turn * 90 + offset) / 360 * 1.1
        sleep(slptim)
        move_actuator(ser_num, rot[i][0] % 2, -offset, rpm)
        if flag:
            move_actuator(rot[i + 1][0] // 2, rot[i + 1][0] % 2, -offset, rpm)
            i += 1
        i += 1
        slptim2 = abs(60 / rpm * offset / 360) * 1.1
        sleep(slptim2)
        print('done', i, 'sleep:', slptim, slptim2)
    solv_time = time() - strt_solv
    solvingtimevar.set(str(round(solv_time, 3)) + 's')
    print('solving time:', solv_time, 's')

Die Motoren 0 und 2 oder 1 und 3 entsprechen U / B- bzw. R / L-Motoren, sodass sie gleichzeitig gedreht werden können. In einem solchen Fall versuchen wir zu beschleunigen, indem wir gleichzeitig drehen.

Wenn ich den Motor drehe, versuche ich auch, auf die Zeit zu warten, in der er sich dreht (1,1-mal).

ATMEGA328P - C++ Der Inhalt von Python war ziemlich schwer. Da das ATMEGA328P-Programm einfach ist, werde ich alles auf einmal zeigen, ohne es zu teilen.

#include <Servo.h>

const long turn_steps = 400;
const int step_dir[2] = {3, 7};
const int step_pul[2] = {4, 8};

char buf[30];
int idx = 0;
long data[3];

Servo servo0;
Servo servo1;

void move_motor(long num, long deg, long spd) {
  bool hl = true;
  if (deg < 0) hl = false;
  digitalWrite(step_dir[num], hl);
  long wait_time = 1000000 * 60 / turn_steps / spd;
  long steps = abs(deg) * turn_steps / 360;
  bool motor_hl = false;
  for (int i = 0; i < steps; i++) {
    motor_hl = !motor_hl;
    digitalWrite(step_pul[num], motor_hl);
    delayMicroseconds(wait_time);
  }
}

void release_arm(int num) {
  if (num == 0)servo0.write(120);
  else servo1.write(120);
}

void grab_arm(int num) {
  if (num == 0)servo0.write(60);
  else servo1.write(60);
}

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 2; i++) {
    pinMode(step_dir[i], OUTPUT);
    pinMode(step_pul[i], OUTPUT);
  }
  servo0.attach(5);
  servo1.attach(6);
}

void loop() {
  if (Serial.available()) {
    buf[idx] = Serial.read();
    if (buf[idx] == '\n') {
      buf[idx] = '\0';
      data[0] = atoi(strtok(buf, " "));
      data[1] = atoi(strtok(NULL, " "));
      data[2] = atoi(strtok(NULL, " "));
      if (data[1] == 1000) grab_arm(data[0]);
      else if (data[1] == 2000) release_arm(data[0]);
      else move_motor(data[0], data[1], data[2]);
      idx = 0;
    }
    else {
      idx++;
    }
  }
}

Ich werde jede Funktion vorstellen.

turning_time(int deg, int speed_motor)

float turning_time(int deg, int speed_motor) {
  return abs(1000 * quarter * deg / turn_steps * 60 / speed_motor);
}

Diese Funktion gibt die Zeit (Millisekunden) zurück, die zum Bewegen des Motors benötigt wird, indem der Winkel und die Bewegungsgeschwindigkeit des Motors eingegeben werden.

move_motor(int num, int deg, int spd)

void move_motor(long num, long deg, long spd) {
  bool hl = true;
  if (deg < 0) hl = false;
  digitalWrite(step_dir[num], hl);
  long wait_time = 1000000 * 60 / turn_steps / spd;
  long steps = abs(deg) * turn_steps / 360;
  bool motor_hl = false;
  for (int i = 0; i < steps; i++) {
    motor_hl = !motor_hl;
    digitalWrite(step_pul[num], motor_hl);
    delayMicroseconds(wait_time);
  }
}

Es ist eine Funktion, die den Schrittmotor bewegt. Ich habe A4988 als Motortreiber verwendet (ca. 1000 Yen für 5 bei Amazon), daher ist der Schreibstil daran angepasst.

Wenn step_dir [num] auf HIGH gesetzt ist, handelt es sich um eine Vorwärtsdrehung und bei LOW um eine Rückwärtsdrehung. Und jedes Mal, wenn Sie einen Impuls an step_pul [num] senden, bewegt er sich Schritt für Schritt. Einzelheiten zur Verwendung von A4988 finden Sie unter hier.

release_arm(int num)

void release_arm(int num) {
  if (num == 0)servo0.write(120);
  else servo1.write(120);
}

Eine Funktion, die den Würfel mit dem Arm freigibt. Stellen Sie den Servowinkel auf 120 Grad ein.

grab_arm(int num)

void grab_arm(int num) {
  if (num == 0)servo0.write(60);
  else servo1.write(60);
}

Es ist eine Funktion, den Würfel mit dem Arm zu greifen. Stellen Sie den Servowinkel auf 60 Grad ein.

loop()

void loop() {
  if (Serial.available()) {
    buf[idx] = Serial.read();
    if (buf[idx] == '\n') {
      buf[idx] = '\0';
      data[0] = atoi(strtok(buf, " "));
      data[1] = atoi(strtok(NULL, " "));
      data[2] = atoi(strtok(NULL, " "));
      if (data[1] == 1000) grab_arm(data[0]);
      else if (data[1] == 2000) release_arm(data[0]);
      else move_motor(data[0], data[1], data[2]);
      idx = 0;
    }
    else {
      idx++;
    }
  }
}

Teilen Sie den durch Leerzeichen getrennten Befehl und führen Sie move_motor, grab_arm oder release_arm aus, indem Sie ihn als Abfrage verwenden.

Zusammenfassung

Vielen Dank für das Lesen! Es ist ein Programm mit insgesamt etwa 800 Zeilen, und es ist schwer einzuführen und zu lesen ... Dieses Mal habe ich nur die Funktionen von Funktionen eingeführt und die Details nicht berührt. Wenn Sie die Hardware-Edition noch nicht gelesen haben, verstehen Sie möglicherweise den Mechanismus um den Arm nicht. Wenn Sie Fragen haben, hinterlassen Sie bitte einen Kommentar.

Recommended Posts

Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 3 Software
Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 2 Algorithmus
Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 1. Übersicht
Lassen Sie uns eine Remote-Rumba erstellen [Software]
Schreiben Sie ein Programm, um den 4x4x4 Rubik Cube zu lösen! 2. Algorithmus
Schreiben Sie ein Programm, um den 4x4x4 Rubik Cube zu lösen! 3. Implementierung
Aktualisierte Software für Rubik Cube Robot 7. Schlüsseloperationen
Aktualisierte Software für Rubik Cube Robot 2. Vorberechnung
Aktualisierte Software für Rubik Cube Robot 3. Lösungssuche
Aktualisierte Software für Rubik Cube Robot 1. Grundfunktionen
Aktualisierte Software für Rubik Cube Robot 6. Maschinenbetrieb (Arduino)
Aktualisierte Software für Rubik Cube Robot 5. Maschinenbedienung (Python)
Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 3 Software
Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 2 Algorithmus
Lassen Sie uns einen Roboter bauen, der den Zauberwürfel löst! 1. Übersicht
Aktualisierte Software für Rubik Cube Robot 7. Schlüsseloperationen
Aktualisierte Software für Rubik Cube Robot 2. Vorberechnung
Aktualisierte Software für Rubik Cube Robot 3. Lösungssuche
Aktualisierte Software für Rubik Cube Robot 1. Grundfunktionen
Schreiben wir ein Programm zur Lösung des Rubik-Würfels (Teil 2: IDA * -Suche)
Ersetzen wir UWSC durch Python (5) Machen wir einen Roboter
Aktualisierte Software für Rubik Cube Robot 6. Maschinenbetrieb (Arduino)
Aktualisierte Software für Rubik Cube Robot 5. Maschinenbedienung (Python)
Erstellen Sie einen BOT, der die Discord-URL verkürzt
Machen wir einen Discord Bot.
Lassen Sie uns die Analyse der sinkenden Daten der Titanic so durchführen
Schreiben Sie ein Programm, um den 4x4x4 Rubik Cube zu lösen! 1. Übersicht
Ein Memo, das das Rucksackproblem mit der gierigen Methode löste
Lassen Sie uns ein Diagramm erstellen, auf das mit IPython geklickt werden kann
Ich habe ein Programm erstellt, das die Fehlersuche in Sekunden löst
Machen wir eine Remote-Rumba [Hardware]
Lassen Sie uns eine GUI mit Python erstellen.
Machen wir einen Spot Sale Service 2
Machen wir einen Blockbruch mit wxPython
Machen wir einen Spot Sale Service 1
[Python] Machen Sie die Funktion zu einer Lambda-Funktion
Lassen Sie uns ein Diagramm mit Python erstellen! !!
Machen wir mit xCAT einen Spacon
Machen wir einen Spot Sale Service 3
Lassen Sie uns mit Python ein Shiritori-Spiel machen
Klasse, die die API von DMM trifft
Lassen Sie uns mit Python langsam sprechen
Lassen Sie uns mit PLY 1 eine einfache Sprache erstellen
Lassen Sie uns mit flask-babel eine mehrsprachige Site erstellen
Erstellen Sie ein Webframework mit Python! (1)
Machen wir mit Pylearn 2 eine dreiäugige KI
Lassen Sie uns eine Kombinationsberechnung mit Python durchführen
Machen wir einen Twitter-Bot mit Python!
[Python] Ein Programm, das die Partitur rundet
Erstellen Sie ein Webframework mit Python! (2)
Lassen Sie uns ein Backend-Plug-In für Errbot erstellen
Lassen Sie uns ein Clustering durchführen, das eine schöne Vogelperspektive auf den Textdatensatz bietet
Erstellen einer Software, die den Android-Bildschirm auf eine PC 2 Real-Time Touch Edition spiegelt
Lassen Sie uns eine einfache Vorlage anzeigen, die ideal für den ersten Django ist
So erstellen Sie einen Raspberry Pi, der die Tweets eines bestimmten Benutzers spricht
So erstellen Sie ein Programm zum Lösen des Rubik Cube ab PC Koshien 2014 Floppy Cube