《2025 DigiKey AI应用创意挑战赛》基于视觉的魔方快速还原机器人
# 项目名称基于视觉的魔方快速还原机器人
## 项目概述
带小孩在小区玩,看到了几个小孩在玩魔方。估计跟我小时候一样,怎么弄也还原不了(当然,我现在还是还原不了!)。我凑近一看,有小孩问我能不能帮他还原。我试了几下,一群小孩惊叹原来连大人也是没办法还原魔方的啊!
后来细想,虽然我不能还原,但是我知道还原魔方是有固定的套路的。也就是公式!既然有固定的公式,那用机器来还原肯定就行得通,而且肯定比人要快。
随后学习了一些相关知识就准备开始做这个项目了。项目采用视觉方案来对魔方的每个面的色块进行识别。识别后使用固定的公式驱动舵机转动对魔方进行还原。
!(https://www.eefocus.com/forum/data/attachment/forum/202601/28/063518duxmmvl9zwet3xxm.png)
## 实物展示
驱动部分采用两个舵机,一个舵机控制水平方向上的旋转,另一个舵机实现魔方翻面和魔方旋转时候的夹持。
!(https://www.eefocus.com/forum/data/attachment/forum/202601/28/063901w9vqpbqp5lbtq922.png)
!(https://www.eefocus.com/forum/data/attachment/forum/202601/28/064051xvw5ziv5uyssn65n.png)
摄像头则安装在顶部的位置上。整机的结构如下:
!(https://www.eefocus.com/forum/data/attachment/forum/202601/28/064646tfohyf6zjj5yeaxa.png)
主要功能代码如下:
```
# 导入库
import tkinter as tk
from tkinter import ttk
import datetime as dt
import time
import os.path
# 关闭GUI时的处理函数
def on_closing():
disp.show_cubotino()
print("\n正在关闭GUI应用\n\n")
camWindow.destroy()
servoWindow.destroy()
servo.servo_off()
close_cam()
time.sleep(0.5)
root.destroy()
# 拍摄并显示图像
def take_image(refresh=1, widgets_freeze=True):
global picamera_initialized, cv2, camera, width, height
if widgets_freeze:
labels = (crop_label, warp_label, cam_files_label)
for label in labels:
disable_widgets(label, relief="sunken")
root.update()
if not picamera_initialized:
cv2, camera, width, height = camera_setup(display=disp, gui_debug=False)
picamera_initialized = True
servo.cam_led_On(1)
frame, w, h = read_camera()
servo.cam_led_Off()
try:
frame2, w2, h2 = crop(frame, width, height, x_l, x_r, y_u, y_b)
frame2, w2, h2 = warp(frame2, w2, h2, warp_fraction, warp_slicing)
for i in range(refresh):
cv2.namedWindow('裁剪与校正后图像')
cv2.moveWindow('裁剪与校正后图像', 0,40)
cv2.imshow('裁剪与校正后图像', frame2)
key = cv2.waitKey(100)
except:
print("裁剪或校正错误,请尝试较温和的参数")
if widgets_freeze:
for label in labels:
enable_widgets(label, relief="raised")
root.update()
# 从文件读取舵机设置
def read_servo_settings_file(fname=''):
try:
srv_settings = settings.get_servos_settings()
return srv_settings
except:
print('读取舵机设置文件时出错')
return {}
# 应用舵机设置到全局变量
def upload_servo_settings(srv_settings):
global t_min_pulse_width, t_max_pulse_width, t_servo_close, t_servo_open
global t_servo_read, t_servo_flip, t_servo_rel_delta
global t_flip_to_close_time, t_close_to_flip_time, t_flip_open_time, t_open_close_time, t_rel_time
global b_min_pulse_width, b_max_pulse_width, b_servo_CCW, b_servo_CW, b_home
global b_rel_CCW, b_rel_CW, b_extra_home_CW, b_extra_home_CCW
global b_spin_time, b_rotate_time, b_rel_time
t_min_pulse_width = srv_settings['t_min_pulse_width']
t_max_pulse_width = srv_settings['t_max_pulse_width']
t_servo_close = srv_settings['t_servo_close']
t_servo_open = srv_settings['t_servo_open']
t_servo_read = srv_settings['t_servo_read']
t_servo_flip = srv_settings['t_servo_flip']
t_servo_rel_delta = srv_settings['t_servo_rel_delta']
t_flip_to_close_time = srv_settings['t_flip_to_close_time']
t_close_to_flip_time = srv_settings['t_close_to_flip_time']
t_flip_open_time = srv_settings['t_flip_open_time']
t_open_close_time = srv_settings['t_open_close_time']
t_rel_time = srv_settings['t_rel_time']
b_min_pulse_width = srv_settings['b_min_pulse_width']
b_max_pulse_width = srv_settings['b_max_pulse_width']
b_servo_CCW = srv_settings['b_servo_CCW']
b_servo_CW = srv_settings['b_servo_CW']
b_home = srv_settings['b_home']
b_rel_CCW = srv_settings['b_rel_CCW']
b_rel_CW = srv_settings['b_rel_CW']
b_extra_home_CW = srv_settings['b_extra_home_CW']
b_extra_home_CCW = srv_settings['b_extra_home_CCW']
b_spin_time = srv_settings['b_spin_time']
b_rotate_time = srv_settings['b_rotate_time']
b_rel_time = srv_settings['b_rel_time']
# 更新舵机设置字典
def update_servo_settings_dict():
srv_settings['t_min_pulse_width'] = str(t_min_pulse_width)
srv_settings['t_max_pulse_width'] = str(t_max_pulse_width)
srv_settings['t_servo_close'] = str(t_servo_close)
srv_settings['t_servo_open'] = str(t_servo_open)
srv_settings['t_servo_read'] = str(t_servo_read)
srv_settings['t_servo_flip'] = str(t_servo_flip)
srv_settings['t_servo_rel_delta'] = str(t_servo_rel_delta)
srv_settings['t_flip_to_close_time'] = str(t_flip_to_close_time)
srv_settings['t_close_to_flip_time'] = str(t_close_to_flip_time)
srv_settings['t_flip_open_time'] = str(t_flip_open_time)
srv_settings['t_open_close_time'] = str(t_open_close_time)
srv_settings['t_rel_time'] = str(t_rel_time)
srv_settings['b_min_pulse_width'] = str(b_min_pulse_width)
srv_settings['b_max_pulse_width'] = str(b_max_pulse_width)
srv_settings['b_servo_CCW'] = str(b_servo_CCW)
srv_settings['b_servo_CW'] = str(b_servo_CW)
srv_settings['b_home'] = str(b_home)
srv_settings['b_rel_CCW'] = str(b_rel_CCW)
srv_settings['b_rel_CW'] = str(b_rel_CW)
srv_settings['b_extra_home_CW'] = str(b_extra_home_CW)
srv_settings['b_extra_home_CCW'] = str(b_extra_home_CCW)
srv_settings['b_spin_time'] = str(b_spin_time)
srv_settings['b_rotate_time'] = str(b_rotate_time)
srv_settings['b_rel_time'] = str(b_rel_time)
return srv_settings
# 加载上一次舵机设置
def load_previous_servo_settings():
srv_settings = settings.load_previous_settings(servo=True)
upload_servo_settings(srv_settings)
update_servo_sliders()
# 保存新舵机设置
def save_new_servo_settings():
global srv_settings
fname = settings.get_servo_settings_fname()
if os.path.exists(fname):
datetime = dt.datetime.now().strftime('%Y%m%d_%H%M%S')
backup_fname = fname[:-4] + '_backup_' + datetime + '.txt'
print("\n保存旧设置到备份文件:", backup_fname)
settings.save_setting(backup_fname, srv_settings, debug=False)
settings.backups_cleanup(fname, n=10)
srv_settings = update_servo_settings_dict()
print("保存当前设置到:", fname)
settings.save_setting(fname, srv_settings, debug=False)
else:
print(f"文件 {fname} 不存在")
# 更新舵机滑块
def update_servo_sliders():
s_top_srv_close.set(t_servo_close)
s_top_srv_rel_delta.set(t_servo_rel_delta)
s_top_srv_open.set(t_servo_open)
s_top_srv_read.set(t_servo_read)
s_top_srv_flip.set(t_servo_flip)
s_top_srv_flip_to_close_time.set(t_flip_to_close_time)
s_top_srv_close_to_flip_time.set(t_close_to_flip_time)
s_top_srv_flip_open_time.set(t_flip_open_time)
s_top_srv_open_close_time.set(t_open_close_time)
s_top_srv_rel_time.set(t_rel_time)
s_btn_srv_min_pulse.set(b_min_pulse_width)
s_btn_srv_max_pulse.set(b_max_pulse_width)
s_btn_srv_CCW.set(b_servo_CCW)
s_btn_srv_home.set(b_home)
s_btn_srv_CW.set(b_servo_CW)
s_btn_srv_rel_CCW.set(b_rel_CCW)
s_btn_srv_rel_CW.set(b_rel_CW)
s_extra_home_CW.set(b_extra_home_CW)
s_extra_home_CCW.set(b_extra_home_CCW)
s_btn_srv_spin_time.set(b_spin_time)
s_btn_srv_rotate_time.set(b_rotate_time)
s_btn_srv_rel_time.set(b_rel_time)
# 获取底部舵机最后位置
def last_btn_srv_pos():
global b_servo_pos
if b_servo_pos == 'CCW':
return b_servo_CCW
elif b_servo_pos == 'CW':
return b_servo_CW
else:
return b_home
# 读取摄像头设置文件
def read_cam_settings_file(fname=''):
try:
cam_settings = settings.get_settings()
return cam_settings
except:
print('读取摄像头设置文件时出错')
return {}
# 应用摄像头设置到全局变量
def upload_cam_settings(cam_settings):
global x_l, x_r, y_u, y_b, warp_fraction, warp_slicing
x_l = cam_settings['x_l']
x_r = cam_settings['x_r']
y_u = cam_settings['y_u']
y_b = cam_settings['y_b']
warp_fraction = cam_settings['warp_fraction']
warp_slicing = cam_settings['warp_slicing']
# 加载上一次摄像头设置
def load_previous_cam_settings():
cam_settings = settings.load_previous_settings(servo=False)
upload_cam_settings(cam_settings)
update_cam_sliders()
# 更新摄像头设置字典
def update_cam_settings_dict():
cam_settings['x_l'] = str(x_l)
cam_settings['x_r'] = str(x_r)
cam_settings['y_u'] = str(y_u)
cam_settings['y_b'] = str(y_b)
cam_settings['warp_fraction'] = str(warp_fraction)
cam_settings['warp_slicing'] = str(warp_slicing)
return cam_settings
# 保存新摄像头设置
def save_new_cam_settings():
global cam_settings
fname = settings.get_settings_fname()
if os.path.exists(fname):
datetime = dt.datetime.now().strftime('%Y%m%d_%H%M%S')
backup_fname = fname[:-4] + '_backup_' + datetime + '.txt'
print("\n保存旧摄像头设置到备份文件:", backup_fname)
settings.save_setting(backup_fname, cam_settings, debug=False)
settings.backups_cleanup(fname, n=10)
cam_settings = update_cam_settings_dict()
print("保存当前摄像头设置到:", fname)
settings.save_setting(fname, cam_settings, debug=False)
else:
print(f"文件 {fname} 不存在")
# 更新摄像头滑块
def update_cam_sliders():
s_crop_l.set(x_l)
s_crop_r.set(x_r)
s_crop_u.set(y_u)
s_crop_b.set(y_b)
s_warp_f.set(warp_fraction)
s_warp_s.set(warp_slicing)
# 滑块值处理函数
def servo_close(val):
global t_servo_close, t_servo_rel_delta, t_servo_pos
t_servo_close = round(float(s_top_srv_close.get()),3)
t_servo_rel = round(t_servo_close - float(s_top_srv_rel_delta.get()),3)
disp.show_on_display('顶部舵机 关闭', str(t_servo_close), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('top', t_servo_close)
time.sleep(t_open_close_time)
servo.servo_to_pos('top', t_servo_rel)
time.sleep(t_rel_time)
disp.show_on_display('顶部舵机 关闭', f'{t_servo_close} ({t_servo_rel})', fs1=30, y2=75, fs2=30)
t_servo_pos = 'close'
btm_srv_widgets_status()
def servo_release(val):
global t_servo_rel_delta, t_servo_pos
t_servo_rel_delta = round(float(s_top_srv_rel_delta.get()),3)
disp.show_on_display('顶部舵机 释放', f'({t_servo_rel_delta})', fs1=25, y2=75, fs2=30)
t_servo_pos = 'close'
btm_srv_widgets_status()
def servo_open(val):
global t_servo_open, t_servo_pos
t_servo_open = round(float(s_top_srv_open.get()),3)
disp.show_on_display('顶部舵机 打开', str(t_servo_open), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('top', t_servo_open)
t_servo_pos = 'open'
btm_srv_widgets_status()
def servo_read(val):
global t_servo_read, t_servo_pos
t_servo_read = round(float(s_top_srv_read.get()),3)
disp.show_on_display('顶部舵机 读取', str(t_servo_read), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('top', t_servo_read)
t_servo_pos = 'read'
btm_srv_widgets_status()
def servo_flip(val):
global t_servo_flip, t_servo_pos
t_servo_flip = round(float(s_top_srv_flip.get()),3)
disp.show_on_display('顶部舵机 翻转', str(t_servo_flip), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('top', t_servo_flip)
t_servo_pos = 'flip'
btm_srv_widgets_status()
def flip_to_close_time(val):
global t_flip_to_close_time
t_flip_to_close_time = round(float(s_top_srv_flip_to_close_time.get()),3)
def servo_rel_close(val):
global t_rel_time
t_rel_time = round(float(s_top_srv_rel_time.get()),3)
def close_to_flip_time(val):
global t_close_to_flip_time
t_close_to_flip_time = round(float(s_top_srv_close_to_flip_time.get()),3)
def flip_open_time(val):
global t_flip_open_time
t_flip_open_time = round(float(s_top_srv_flip_open_time.get()),3)
def open_close_time(val):
global t_open_close_time
t_open_close_time = round(float(s_top_srv_open_close_time.get()),3)
def btn_srv_min_pulse(val):
global b_min_pulse_width, b_servo_pos
b_min_pulse_width = round(float(s_btn_srv_min_pulse.get()),3)
disp.show_on_display('底部舵机 最小脉宽', str(b_min_pulse_width), fs1=26, y2=75, fs2=30)
pos = last_btn_srv_pos()
servo.b_servo_create(b_min_pulse_width, b_max_pulse_width, pos)
def btn_srv_max_pulse(val):
global b_max_pulse_width, b_servo_pos
b_max_pulse_width = round(float(s_btn_srv_max_pulse.get()),3)
disp.show_on_display('底部舵机 最大脉宽', str(b_max_pulse_width), fs1=26, y2=75, fs2=30)
pos = last_btn_srv_pos()
servo.b_servo_create(b_min_pulse_width, b_max_pulse_width, pos)
def servo_CCW(val):
global b_servo_CCW, b_servo_pos
if t_servo_pos in ('close', 'open'):
b_servo_CCW = round(float(s_btn_srv_CCW.get()),3)
disp.show_on_display('底部舵机 逆时针', str(b_servo_CCW), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('bottom', b_servo_CCW)
b_servo_pos = 'CCW'
else:
disp.show_on_display('顶部舵机', '阻挡中', fs1=30, y2=75, fs2=30)
def servo_home(val):
global b_home, b_servo_pos
if t_servo_pos in ('close', 'open'):
b_home = round(float(s_btn_srv_home.get()),3)
disp.show_on_display('底部舵机 归位', str(b_home), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('bottom', b_home)
b_servo_pos = 'home'
else:
disp.show_on_display('顶部舵机', '阻挡中', fs1=30, y2=75, fs2=30)
def servo_CW(val):
global b_servo_CW, b_servo_pos
if t_servo_pos in ('close', 'open'):
b_servo_CW = round(float(s_btn_srv_CW.get()),3)
disp.show_on_display('底部舵机 顺时针', str(b_servo_CW), fs1=30, y2=75, fs2=30)
servo.servo_to_pos('bottom', b_servo_CW)
b_servo_pos = 'CW'
else:
disp.show_on_display('顶部舵机', '阻挡中', fs1=30, y2=75, fs2=30)
def servo_rel_CCW(val):
global b_rel_CCW
b_rel_CCW = round(float(s_btn_srv_rel_CCW.get()),3)
target = round(b_servo_CCW + b_rel_CCW,3)
servo.servo_to_pos('bottom', target)
disp.show_on_display('逆时针释放', str(b_rel_CCW), fs1=30, y2=75, fs2=30)
def servo_rel_CW(val):
global b_rel_CW
b_rel_CW = round(float(s_btn_srv_rel_CW.get()),3)
disp.show_on_display('顺时针释放', str(b_rel_CW), fs1=30, y2=75, fs2=30)
target = round(b_servo_CW - b_rel_CW,3)
servo.servo_to_pos('bottom', target)
b_servo_pos = 'CW'
def servo_extra_home_CCW(val):
global b_extra_home_CCW, b_servo_pos
b_extra_home_CCW = round(float(s_extra_home_CCW.get()),3)
disp.show_on_display('归位额外逆时针', str(b_extra_home_CCW), fs1=30, y2=75, fs2=30)
target = round(b_home + b_extra_home_CCW,3)
servo.servo_to_pos('bottom', target)
b_servo_pos = 'CW'
def servo_extra_home_CW(val):
global b_extra_home_CW, b_servo_pos
b_extra_home_CW = round(float(s_extra_home_CW.get()),3)
disp.show_on_display('归位额外顺时针', str(b_extra_home_CW), fs1=30, y2=75, fs2=30)
target = round(b_home - b_extra_home_CW,3)
servo.servo_to_pos('bottom', target)
b_servo_pos = 'CW'
def servo_rotate_time(val):
global b_rotate_time
b_rotate_time = round(float(s_btn_srv_rotate_time.get()),3)
def servo_rel_time(val):
global b_rel_time
b_rel_time = round(float(s_btn_srv_rel_time.get()),3)
def servo_spin_time(val):
global b_spin_time
b_spin_time = round(float(s_btn_srv_spin_time.get()),3)
def crop_u(val):
global y_u
y_u = int(s_crop_u.get())
disp.show_on_display('裁剪 上边', str(y_u), fs1=27, y2=75, fs2=30)
take_image(widgets_freeze=False)
def crop_l(val):
global x_l
x_l = int(s_crop_l.get())
disp.show_on_display('裁剪 左边', str(x_l), fs1=28, y2=75, fs2=30)
take_image(widgets_freeze=False)
def crop_r(val):
global x_r
x_r = int(s_crop_r.get())
disp.show_on_display('裁剪 右边', str(x_r), fs1=28, y2=75, fs2=30)
take_image(widgets_freeze=False)
def crop_b(val):
global y_b
y_b = int(s_crop_b.get())
disp.show_on_display('裁剪 下边', str(y_b), fs1=26, y2=75, fs2=30)
take_image(widgets_freeze=False)
def warp_f_get(val):
global warp_fraction
warp_fraction = float(round(s_warp_f.get(),2))
disp.show_on_display('校正比例', str(warp_fraction), fs1=24, y2=75, fs2=30)
take_image(widgets_freeze=False)
def warp_s_get(val):
global warp_slicing
warp_slicing = float(s_warp_s.get())
disp.show_on_display('校正切片', str(warp_slicing), fs1=26, y2=75, fs2=30)
take_image(widgets_freeze=False)
# 测试设置
def servo_init():
global t_servo_pos, b_servo_pos
servo_init=servo.servo_tune_via_gui(debug=False, start_pos = 'open')
if servo_init:
t_servo_pos = 'start'
b_servo_pos = 'home'
def close_top_cover():
global t_servo_pos
servo.stop_release()
if b_servo_pos in ('CCW', 'home', 'CW'):
disp.show_on_display('顶部舵机 关闭', str(t_servo_close), fs1=30, y2=75, fs2=30)
t_servo_pos = servo.close_cover(t_servo_close, t_servo_rel_delta, t_open_close_time, t_rel_time, test=True)
disp.show_on_display('顶部舵机 关闭', f'{t_servo_close} ({t_servo_rel_delta})', fs1=30, y2=75, fs2=30)
btm_srv_widgets_status()
def open_top_cover():
global t_servo_pos
servo.stop_release()
if b_servo_pos in ('CCW', 'home', 'CW') and t_servo_pos != 'open':
t_servo_pos = servo.open_cover(t_servo_open, test=True)
disp.show_on_display('顶部舵机 打开', str(t_servo_open), fs1=30, y2=75, fs2=30)
btm_srv_widgets_status()
def read_position():
global t_servo_read, t_servo_pos
servo.stop_release()
if b_servo_pos in ('CCW', 'home', 'CW') and t_servo_pos != 'read':
t_servo_pos = servo.servo_to_pos('top', t_servo_read)
disp.show_on_display('顶部舵机 读取', str(t_servo_read), fs1=30, y2=75, fs2=30)
btm_srv_widgets_status()
def flip_cube():
global t_servo_pos
servo.stop_release()
if b_servo_pos in ('CCW', 'home', 'CW'):
t_servo_pos = servo.flip_toggle(t_servo_pos, t_servo_read, t_servo_flip)
if t_servo_pos == 'flip':
disp.show_on_display('顶部舵机 翻转', str(t_servo_flip), fs1=30, y2=75, fs2=30)
if t_servo_pos == 'read':
disp.show_on_display('顶部舵机 读取', str(t_servo_read), fs1=30, y2=75, fs2=30)
btm_srv_widgets_status()
def ccw():
global b_servo_pos
servo.stop_release()
if t_servo_pos == 'open':
timer1 = b_spin_time
if b_servo_pos == 'CW':
timer1+=timer1
if t_servo_pos in ('close', 'open') and b_servo_pos != 'CCW':
target = round(b_servo_CCW + b_rel_CCW,3)
b_servo_pos = servo.spin_out('CCW', target, 0, timer1, test=True )
disp.show_on_display('底部舵机 逆时针', f'({target})' , fs1=30, y2=75, fs2=30)
elif t_servo_pos == 'close':
timer1 = b_rotate_time
if b_servo_pos == 'CW':
timer1+=timer1
if t_servo_pos in ('close', 'open') and b_servo_pos != 'CCW':
b_servo_pos = servo.spin_out('CCW', b_servo_CCW, b_rel_CCW, timer1, test=True )
disp.show_on_display('底部舵机 逆时针', f'{b_servo_CCW} ({round(b_servo_CCW+b_rel_CCW,3)})', fs1=30, y2=75, fs2=30)
def home():
global b_servo_pos
if t_servo_pos in ('close', 'open') and b_servo_pos != 'home':
servo.stop_release()
if t_servo_pos == 'open':
timer1 = b_spin_time
if b_servo_pos == 'CCW':
b_servo_pos = servo.rotate_home('CW', b_home, 0, timer1, test=True)
disp.show_on_display('顶部舵机 归位', str(b_home), fs1=30, y2=75, fs2=30)
elif b_servo_pos == 'CW':
b_servo_pos = servo.rotate_home('CCW', b_home, 0, timer1, test=True)
disp.show_on_display('顶部舵机 归位', str(b_home), fs1=30, y2=75, fs2=30)
elif t_servo_pos == 'close':
timer1 = b_rotate_time
if b_servo_pos == 'CCW':
b_servo_pos = servo.rotate_home('CW', b_home, b_extra_home_CCW, timer1, test=True)
disp.show_on_display('顶部舵机 归位', f'{round(b_home+b_extra_home_CCW,3)} ({b_home})', fs1=30, y2=75, fs2=30)
elif b_servo_pos == 'CW':
b_servo_pos = servo.rotate_home('CCW', b_home, b_extra_home_CW, timer1, test=True)
disp.show_on_display('顶部舵机 归位', f'{round(b_home-b_extra_home_CW,3)} ({b_home})', fs1=30, y2=75, fs2=30)
def cw():
global b_servo_pos
if t_servo_pos in ('close', 'open') and b_servo_pos != 'CW':
servo.stop_release()
if t_servo_pos == 'open':
timer1 = b_spin_time
if b_servo_pos == 'CCW':
timer1+=timer1
target = round(b_servo_CW - b_rel_CW, 3)
b_servo_pos = servo.spin_out('CW', target, 0, timer1, test=True)
disp.show_on_display('底部舵机 逆时针', f'({target})', fs1=30, y2=75, fs2=30)
elif t_servo_pos == 'close':
timer1 = b_rotate_time
if b_servo_pos == 'CCW':
timer1+=timer1
b_servo_pos = servo.spin_out('CW', b_servo_CW, b_rel_CW, timer1, test=True)
disp.show_on_display('底部舵机 逆时针', f'{b_servo_CW} ({round(b_servo_CW-b_rel_CW,3)})', fs1=30, y2=75, fs2=30)
print("b_servo_CW, b_rel_CW, timer1:", b_servo_CW, b_rel_CW, timer1)
def test():
for label in (top_srv_label, btn_srv_label, test_label, large_test_label, files_label):
disable_widgets(label, relief="sunken")
root.update()
servo.stop_release()
read_position()
result = servo.test_set_of_movements()
if result == 'stopped':
servo_init()
elif result == 'completed':
open_top_cover()
time.sleep(0.5)
home()
for label in (top_srv_label, btn_srv_label):
enable_widgets(label, relief="raised")
for label in (test_label, large_test_label, files_label):
enable_widgets(label, relief="raised")
btm_srv_widgets_status()
def btm_srv_widgets_status():
global t_servo_pos
if t_servo_pos == 'close' or t_servo_pos == 'open':
ccw_btn["relief"] = "raised"
ccw_btn["state"] = "normal"
home_btn["relief"] = "raised"
home_btn["state"] = "normal"
cw_btn["relief"] = "raised"
cw_btn["state"] = "normal"
enable_widgets(btn_srv_label, relief="raised")
else:
ccw_btn["relief"] = "sunken"
ccw_btn["state"] = "disabled"
home_btn["relief"] = "sunken"
home_btn["state"] = "disabled"
cw_btn["relief"] = "sunken"
cw_btn["state"] = "disabled"
disable_widgets(btn_srv_label, relief="sunken")
# GUI窗口管理
def goto_camWindow():
show_window(camWindow)
def goto_servoWindow():
show_window(servoWindow)
def disable_widgets(parent, relief):
for child in parent.winfo_children():
child["relief"] = relief
child["state"] = "disabled"
def enable_widgets(parent, relief):
for child in parent.winfo_children():
child["relief"] = relief
child["state"] = "normal"
def show_window(window, startup=False):
global servoWindow_ontop, t_servo_pos
if window==servoWindow:
if not startup:
try:
open_top_cover()
t_servo_pos = ''
btm_srv_widgets_status()
except:
pass
window.tkraise()
servoWindow_ontop=True
root.geometry('+0+40')
if not startup:
root.resizable(1,1)
root.geometry(f'{srv_w_w}x{srv_w_h}')
cv2.destroyAllWindows()
disp.show_on_display('舵机', '调校中', fs1=38, y2=75, fs2=38)
disp.set_backlight(1)
return
elif window==camWindow:
read_position()
window.tkraise()
servoWindow_ontop=False
w = 1100
h = 650
root.resizable(1,1)
root.geometry(f'{w}x{h}+{int(ws-w)}+40')
root.update()
update_cam_sliders()
root.update()
disp.show_on_display('摄像头', '调校中', fs1=38, y2=75, fs2=38)
disp.set_backlight(1)
take_image(refresh=10)
# 初始变量
printout = False
servoWindow_ontop=True
picamera_initialized = False
startup = True
srv_settings= read_servo_settings_file()
upload_servo_settings(srv_settings)
servo_init()
cam_settings= read_cam_settings_file()
upload_cam_settings(cam_settings)
# GUI高层部分
root = tk.Tk()
root.title("CUBOTino 舵机设置")
root.geometry('+0+40')
root.rowconfigure(0, weight=1)
root.columnconfigure(0,weight=1)
root.resizable(1,1)
servoWindow=tk.Frame(root)
camWindow=tk.Frame(root)
for window in (servoWindow, camWindow):
window.grid(row=0,column=0,sticky='nsew')
show_window(servoWindow, startup)
# 顶部舵机相关部件
top_srv_label = tk.LabelFrame(servoWindow, text="顶部盖板 - 舵机设置", labelanchor="nw", font=("Arial", "
```
!(https://www.eefocus.com/forum/data/attachment/forum/202601/28/065556dg6nx4xy8ne6x6ts.png)
## 后记
本次分享项目主要参考资源为CUBOTino_micro的外形结构和这个开源项目的魔方解法代码。
最后感谢eefocus, 感谢Digkey, 让我成为最酷的孩子王。
页:
[1]