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
GitHub ist hier.
Das Werbevideo ist hier.
Die Videosammlung zu dieser Artikelsammlung ist hier.
In diesem Artikel werde ich die Software von Soltvvo erklären, einem Roboter, der den von mir erstellten 2x2x2-Rubikwürfel löst.
Bevor die Software erklärt wird, muss notiert werden, welche Art von Konfiguration der Roboter ausführen wird. Es sieht aus wie die Figur.
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.
Ich werde Ihnen das ganze Bild des Programms zeigen.
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.
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
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.
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.
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.
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.
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. 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.
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.
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.
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.
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
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
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.
#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.
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.
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.
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