ezgo 0.0.5__py3-none-any.whl → 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ezgo/__init__.py +7 -1
- ezgo/ezcamera.py +181 -0
- ezgo/eztk.py +219 -0
- ezgo-0.0.7.dist-info/METADATA +544 -0
- ezgo-0.0.7.dist-info/RECORD +13 -0
- ezgo-0.0.5.dist-info/METADATA +0 -249
- ezgo-0.0.5.dist-info/RECORD +0 -11
- {ezgo-0.0.5.dist-info → ezgo-0.0.7.dist-info}/LICENSE +0 -0
- {ezgo-0.0.5.dist-info → ezgo-0.0.7.dist-info}/WHEEL +0 -0
- {ezgo-0.0.5.dist-info → ezgo-0.0.7.dist-info}/top_level.txt +0 -0
ezgo/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ ezgo - 宇树Go2机器狗Python控制库
|
|
|
8
8
|
支持运动控制、视频流获取、UI界面等功能。
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
__version__ = "0.0.
|
|
11
|
+
__version__ = "0.0.7"
|
|
12
12
|
__author__ = "ezgo"
|
|
13
13
|
__email__ = ""
|
|
14
14
|
__license__ = "MIT"
|
|
@@ -20,6 +20,8 @@ try:
|
|
|
20
20
|
from .ui import APP
|
|
21
21
|
from .go2_camera import Go2Camera
|
|
22
22
|
from .go2_vui import Go2VUI
|
|
23
|
+
from .eztk import EasyTk
|
|
24
|
+
from .ezcamera import Camera as EzCamera
|
|
23
25
|
except ImportError as e:
|
|
24
26
|
print(f"警告: 无法导入部分模块: {e}")
|
|
25
27
|
print("请确保已安装所有依赖包")
|
|
@@ -28,6 +30,8 @@ except ImportError as e:
|
|
|
28
30
|
APP = None
|
|
29
31
|
Go2Camera = None
|
|
30
32
|
Go2VUI = None
|
|
33
|
+
EasyTk = None
|
|
34
|
+
EzCamera = None
|
|
31
35
|
|
|
32
36
|
# 定义公开的API
|
|
33
37
|
__all__ = [
|
|
@@ -36,6 +40,8 @@ __all__ = [
|
|
|
36
40
|
"APP",
|
|
37
41
|
"Go2Camera",
|
|
38
42
|
"Go2VUI",
|
|
43
|
+
"EasyTk",
|
|
44
|
+
"EzCamera",
|
|
39
45
|
"__version__"
|
|
40
46
|
]
|
|
41
47
|
|
ezgo/ezcamera.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
from PIL import Image, ImageTk
|
|
3
|
+
import platform
|
|
4
|
+
import time
|
|
5
|
+
import queue
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Camera:
|
|
10
|
+
def __init__(self, index=0, width=640, height=480, fps=30,):
|
|
11
|
+
"""
|
|
12
|
+
初始化摄像头对象。
|
|
13
|
+
|
|
14
|
+
参数:
|
|
15
|
+
- index: 摄像头索引,默认值为 0。
|
|
16
|
+
- width: 视频宽度,默认值为 640。
|
|
17
|
+
- height: 视频高度,默认值为 480。
|
|
18
|
+
- fps: 视频帧率,默认值为 30。
|
|
19
|
+
"""
|
|
20
|
+
# 基础参数
|
|
21
|
+
self.index = index
|
|
22
|
+
self.width = width
|
|
23
|
+
self.height = height
|
|
24
|
+
self.fps = fps
|
|
25
|
+
|
|
26
|
+
# 状态控制
|
|
27
|
+
self.is_opened = False # 摄像头是否成功打开
|
|
28
|
+
self.is_running = False # 线程退出标志
|
|
29
|
+
self.first_frame_ready = threading.Event() # 第一帧就绪事件
|
|
30
|
+
|
|
31
|
+
# 资源管理
|
|
32
|
+
self.cap = None
|
|
33
|
+
self.capture_thread = None # 读帧线程对象
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# 双缓冲:两个缓冲区
|
|
37
|
+
self.buffer_write = None # 写入缓冲区
|
|
38
|
+
self.buffer_read = None # 读取缓冲区
|
|
39
|
+
self.buffer_lock = threading.Lock() # 用于交换缓冲区的锁
|
|
40
|
+
|
|
41
|
+
# 线程锁
|
|
42
|
+
# self.lock = threading.Lock()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def open(self, timeout=5):
|
|
47
|
+
"""
|
|
48
|
+
打开摄像头。
|
|
49
|
+
若在 Linux 系统下,启用 V4L2 硬件加速和 MJPG 压缩格式。
|
|
50
|
+
|
|
51
|
+
参数:
|
|
52
|
+
- timeout: 等待第一帧图像的超时时间,默认值为 5 秒。
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
if self.is_opened:
|
|
56
|
+
print(f"摄像头 {self.index} 已打开")
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
if platform.system() == "Linux":
|
|
60
|
+
self.cap = cv2.VideoCapture(self.index, cv2.CAP_V4L2)
|
|
61
|
+
# 尝试设置MJPG格式(硬件加速)
|
|
62
|
+
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
|
63
|
+
self.cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
|
64
|
+
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # 减少摄像头缓冲区,降低延迟
|
|
65
|
+
elif platform.system() == "Windows":
|
|
66
|
+
self.cap = cv2.VideoCapture(self.index, cv2.CAP_DSHOW) # 替代默认的CAP_MSMF,降低延迟
|
|
67
|
+
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
|
68
|
+
self.cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
|
69
|
+
else: # macOS
|
|
70
|
+
self.cap = cv2.VideoCapture(self.index)
|
|
71
|
+
self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
|
|
72
|
+
|
|
73
|
+
if not self.cap.isOpened():
|
|
74
|
+
raise Exception("无法打开摄像头")
|
|
75
|
+
|
|
76
|
+
# 仅当实际分辨率不一致时才设置分辨率
|
|
77
|
+
actual_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
78
|
+
actual_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
79
|
+
if actual_width != self.width or actual_height != self.height:
|
|
80
|
+
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
|
|
81
|
+
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
|
|
82
|
+
|
|
83
|
+
# 重置状态
|
|
84
|
+
self.first_frame_ready.clear()
|
|
85
|
+
self.buffer_write = None
|
|
86
|
+
self.buffer_read = None
|
|
87
|
+
|
|
88
|
+
# 启动读帧线程
|
|
89
|
+
self.is_running = True
|
|
90
|
+
self.capture_thread = threading.Thread(target=self._capture_thread, daemon=True)
|
|
91
|
+
self.capture_thread.start()
|
|
92
|
+
|
|
93
|
+
# 等待第一帧就绪(带超时)
|
|
94
|
+
if not self.first_frame_ready.wait(timeout=timeout):
|
|
95
|
+
self.close()
|
|
96
|
+
raise TimeoutError(f"等待第一帧超时({timeout}秒)")
|
|
97
|
+
|
|
98
|
+
self.is_opened = True
|
|
99
|
+
|
|
100
|
+
return self
|
|
101
|
+
|
|
102
|
+
def _capture_thread(self):
|
|
103
|
+
"""读取图像帧线程"""
|
|
104
|
+
frame_interval = 1.0 / self.fps # 理论帧间隔
|
|
105
|
+
first_frame = True
|
|
106
|
+
next_frame_time = time.time() # 初始化
|
|
107
|
+
while self.is_running and self.cap.isOpened():
|
|
108
|
+
start_time = time.time()
|
|
109
|
+
# 读取帧
|
|
110
|
+
ret, frame = self.cap.read()
|
|
111
|
+
if not ret:
|
|
112
|
+
time.sleep(0.001)
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
# 写入缓冲区
|
|
116
|
+
with self.buffer_lock:
|
|
117
|
+
# 先交换缓冲区:把已写好的交给读缓冲区
|
|
118
|
+
self.buffer_write, self.buffer_read = self.buffer_read, self.buffer_write
|
|
119
|
+
# 再写入新帧到空的写缓冲区
|
|
120
|
+
self.buffer_write = frame
|
|
121
|
+
|
|
122
|
+
# 标记第一帧就绪
|
|
123
|
+
if first_frame and self.buffer_read is not None:
|
|
124
|
+
self.first_frame_ready.set()
|
|
125
|
+
first_frame = False
|
|
126
|
+
|
|
127
|
+
# 基于绝对时间的帧率控制,避免累积误差
|
|
128
|
+
next_frame_time += frame_interval
|
|
129
|
+
sleep_time = max(0, next_frame_time - time.time())
|
|
130
|
+
# 短睡眠用time.sleep,长睡眠用cv2.waitKey(更低CPU占用)
|
|
131
|
+
if sleep_time > 0.01:
|
|
132
|
+
cv2.waitKey(int(sleep_time * 1000)) # 毫秒级,释放GIL
|
|
133
|
+
else:
|
|
134
|
+
time.sleep(sleep_time)
|
|
135
|
+
|
|
136
|
+
def read(self):
|
|
137
|
+
"""读取最新帧"""
|
|
138
|
+
frame = None
|
|
139
|
+
with self.buffer_lock:
|
|
140
|
+
if self.buffer_read is not None:
|
|
141
|
+
frame = self.buffer_read # 先引用,不拷贝
|
|
142
|
+
return frame.copy() if frame is not None else None # 仅当有数据时拷贝
|
|
143
|
+
|
|
144
|
+
def save_image(self, frame, filename="frame.jpg"):
|
|
145
|
+
"""保存当前帧到文件"""
|
|
146
|
+
if frame is None:
|
|
147
|
+
raise ValueError("无法保存空帧")
|
|
148
|
+
cv2.imwrite(filename, frame)
|
|
149
|
+
|
|
150
|
+
def resize(self, frame, size=(224, 224)):
|
|
151
|
+
"""将图像调整为指定尺寸"""
|
|
152
|
+
if frame is None:
|
|
153
|
+
return None
|
|
154
|
+
return cv2.resize(frame, size, interpolation=cv2.INTER_LINEAR)
|
|
155
|
+
|
|
156
|
+
def crop_to_square(self, frame):
|
|
157
|
+
"""将图像裁剪为正方形"""
|
|
158
|
+
if frame is None:
|
|
159
|
+
return None
|
|
160
|
+
h, w = frame.shape[:2]
|
|
161
|
+
min_dim = min(h, w)
|
|
162
|
+
y = (h - min_dim) // 2
|
|
163
|
+
x = (w - min_dim) // 2
|
|
164
|
+
return frame[y:y+min_dim, x:x+min_dim]
|
|
165
|
+
|
|
166
|
+
def close(self):
|
|
167
|
+
"""手动释放资源"""
|
|
168
|
+
self.is_running = False # 停止读帧线程
|
|
169
|
+
self.is_opened = False
|
|
170
|
+
|
|
171
|
+
if self.capture_thread is not None:
|
|
172
|
+
self.capture_thread.join(timeout=1.0) # 等待线程退出
|
|
173
|
+
self.capture_thread = None
|
|
174
|
+
|
|
175
|
+
if self.cap is not None:
|
|
176
|
+
self.cap.release()
|
|
177
|
+
self.cap = None
|
|
178
|
+
|
|
179
|
+
def __del__(self):
|
|
180
|
+
"""释放资源"""
|
|
181
|
+
self.close()
|
ezgo/eztk.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import numpy as np
|
|
3
|
+
import cv2
|
|
4
|
+
from PIL import Image, ImageTk
|
|
5
|
+
import platform
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
class EasyTk:
|
|
9
|
+
def __init__(self, title="APP", size=None):
|
|
10
|
+
"""
|
|
11
|
+
初始化EasyTk窗口
|
|
12
|
+
:param title: 窗口标题
|
|
13
|
+
:param size: 窗口大小(格式:"宽度x高度",例如"480x800")
|
|
14
|
+
"""
|
|
15
|
+
self.root = tk.Tk()
|
|
16
|
+
self.root.title(title)
|
|
17
|
+
self.root.geometry(size)
|
|
18
|
+
|
|
19
|
+
if platform.system() == "Linux": # 灵芯派默认设置
|
|
20
|
+
if size is None:
|
|
21
|
+
self.root.geometry("480x800") # 窗口分辨率480*800
|
|
22
|
+
self.root.resizable(False, False) # 固定窗口大小
|
|
23
|
+
self.root.attributes('-fullscreen', True) # 全屏
|
|
24
|
+
self.root.config(cursor="none") # 隐藏鼠标
|
|
25
|
+
|
|
26
|
+
# self.root.update_idletasks() # 强制立即应用全屏,避免闪烁/延迟
|
|
27
|
+
|
|
28
|
+
self._image = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def add_frame(self, master=None, **kwargs):
|
|
32
|
+
"""
|
|
33
|
+
添加一个框架(LabelFrame)到窗口中
|
|
34
|
+
|
|
35
|
+
:param master: 父容器(默认是root)
|
|
36
|
+
:param kwargs: 组件配置和布局参数
|
|
37
|
+
组件配置常用参数:
|
|
38
|
+
- width: 宽度(像素),如果为空则根据内容自适应
|
|
39
|
+
- height: 高度(像素),如果为空则根据内容自适应
|
|
40
|
+
- bg: 背景颜色,例如"white"
|
|
41
|
+
组件布局常用参数:
|
|
42
|
+
- side: 组件在父容器中的位置,tk.TOP(上)、tk.BOTTOM(下)、tk.LEFT(左)、tk.RIGHT(右)
|
|
43
|
+
- fill: 组件在父容器中的填充方式,tk.BOTH(填充父容器)、tk.X(填充水平方向)、tk.Y(填充垂直方向)
|
|
44
|
+
- expand: 是否允许组件在父容器中扩展,True(允许)、False(不允许)
|
|
45
|
+
- anchor: 组件在父容器中的对齐方式,tk.N(北)、tk.S(南)、tk.E(东)、tk.W(西)、tk.CENTER(居中)
|
|
46
|
+
- padx: 组件在父容器中的水平间距,单位像素
|
|
47
|
+
- pady: 组件在父容器中的垂直间距,单位像素
|
|
48
|
+
|
|
49
|
+
:return: 新添加的框架(LabelFrame)
|
|
50
|
+
:rtype: LabelFrame
|
|
51
|
+
"""
|
|
52
|
+
kwargs['master'] = master if master else self.root
|
|
53
|
+
pack_kwargs = {
|
|
54
|
+
'side': kwargs.pop('side', tk.TOP),
|
|
55
|
+
'fill': kwargs.pop('fill', tk.BOTH),
|
|
56
|
+
'expand': kwargs.pop('expand', False if kwargs['master'] == self.root else True),
|
|
57
|
+
'anchor': kwargs.pop('anchor', tk.CENTER),
|
|
58
|
+
'padx': kwargs.pop('padx', 0),
|
|
59
|
+
'pady': kwargs.pop('pady', 0)}
|
|
60
|
+
|
|
61
|
+
frame = tk.Frame(**kwargs)
|
|
62
|
+
if kwargs.get('width', None) and kwargs.get('height', None):
|
|
63
|
+
pack_kwargs['fill'] = tk.NONE
|
|
64
|
+
elif kwargs.get('width', None):
|
|
65
|
+
pack_kwargs['fill'] = tk.Y
|
|
66
|
+
elif kwargs.get('height', None):
|
|
67
|
+
pack_kwargs['fill'] = tk.X
|
|
68
|
+
|
|
69
|
+
frame.pack_propagate(False if (kwargs.get('width', None) or kwargs.get('height', None)) else True)
|
|
70
|
+
frame.pack(**pack_kwargs)
|
|
71
|
+
|
|
72
|
+
return frame
|
|
73
|
+
|
|
74
|
+
def add_label(self, master=None, **kwargs):
|
|
75
|
+
"""
|
|
76
|
+
添加一个标签(Label)到窗口中, 可以显示文本或图片
|
|
77
|
+
|
|
78
|
+
:param master: 父容器(默认是root)
|
|
79
|
+
:param kwargs: 组件配置和布局参数
|
|
80
|
+
组件配置常用参数:
|
|
81
|
+
- text: 显示的文本内容
|
|
82
|
+
- image: 显示的图片路径、opencv图像(np.array)、PIL.Image.Image对象
|
|
83
|
+
- font: 字体设置,例如("SimHei", 24)
|
|
84
|
+
- fg: 字体颜色,例如"red"
|
|
85
|
+
- bg: 背景颜色,例如"white"
|
|
86
|
+
- width: 宽度(像素),如果为空则根据内容自适应
|
|
87
|
+
- height: 高度(像素),如果为空则根据内容自适应
|
|
88
|
+
- wraplength: 文本换行长度,单位像素(默认0,不换行)
|
|
89
|
+
组件布局常用参数:
|
|
90
|
+
- side: 组件在父容器中的位置,tk.TOP(上)、tk.BOTTOM(下)、tk.LEFT(左)、tk.RIGHT(右)
|
|
91
|
+
- fill: 组件在父容器中的填充方式,tk.BOTH(填充父容器)、tk.X(填充水平方向)、tk.Y(填充垂直方向)
|
|
92
|
+
- expand: 是否允许组件在父容器中扩展,True(允许)、False(不允许)
|
|
93
|
+
- anchor: 组件在父容器中的对齐方式,tk.N(北)、tk.S(南)、tk.E(东)、tk.W(西)、tk.CENTER(居中)
|
|
94
|
+
- padx: 组件在父容器中的水平间距,单位像素
|
|
95
|
+
- pady: 组件在父容器中的垂直间距,单位像素
|
|
96
|
+
:return: 新添加的标签(Label)
|
|
97
|
+
:rtype: Label
|
|
98
|
+
"""
|
|
99
|
+
# 在Label外部添加一个Frame,用于布局控制
|
|
100
|
+
master = master if master else self.root
|
|
101
|
+
_kwargs = {
|
|
102
|
+
"side" : kwargs.pop('side', tk.TOP), # 堆叠方向
|
|
103
|
+
"fill" : kwargs.pop('fill', tk.BOTH), # 填充方向
|
|
104
|
+
"expand" : kwargs.pop('expand', False if master == self.root else True) , # 是否展开填充
|
|
105
|
+
"anchor" : kwargs.pop('anchor', tk.CENTER),
|
|
106
|
+
"padx" : kwargs.pop('padx', 0),
|
|
107
|
+
"pady" : kwargs.pop('pady', 0),
|
|
108
|
+
"width" : kwargs.pop('width', None),
|
|
109
|
+
"height" : kwargs.pop('height', None),
|
|
110
|
+
"bg": kwargs.get('bg', None),
|
|
111
|
+
}
|
|
112
|
+
_frame = self.add_frame(master, **_kwargs)
|
|
113
|
+
|
|
114
|
+
# 在Frame内部创建Label组件
|
|
115
|
+
image = kwargs.pop('image', None)
|
|
116
|
+
label = tk.Label(_frame, **kwargs)
|
|
117
|
+
if image is not None: # 配置图片
|
|
118
|
+
self.config(label, image=image)
|
|
119
|
+
label.pack(fill=tk.BOTH, expand=True) # 放置组件
|
|
120
|
+
return label
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def add_button(self, master=None, **kwargs):
|
|
124
|
+
"""
|
|
125
|
+
添加一个按钮(Button)到窗口中, 可以显示文本或图片
|
|
126
|
+
|
|
127
|
+
:param master: 父容器(默认是root)
|
|
128
|
+
:param kwargs: 组件配置和布局参数
|
|
129
|
+
组件配置常用参数:
|
|
130
|
+
- text: 显示的文本内容
|
|
131
|
+
- image: 显示的图片路径、opencv图像(np.array)、PIL.Image.Image对象
|
|
132
|
+
- font: 字体设置,例如("SimHei", 24)
|
|
133
|
+
- fg: 字体颜色,例如"red"
|
|
134
|
+
- bg: 背景颜色,例如"white"
|
|
135
|
+
- width: 宽度(像素),如果为空则根据内容自适应
|
|
136
|
+
- height: 高度(像素),如果为空则根据内容自适应
|
|
137
|
+
- wraplength: 文本换行长度,单位像素(默认0,不换行)
|
|
138
|
+
组件布局常用参数:
|
|
139
|
+
- side: 组件在父容器中的位置,tk.TOP(上)、tk.BOTTOM(下)、tk.LEFT(左)、tk.RIGHT(右)
|
|
140
|
+
- fill: 组件在父容器中的填充方式,tk.BOTH(填充父容器)、tk.X(填充水平方向)、tk.Y(填充垂直方向)
|
|
141
|
+
- expand: 是否允许组件在父容器中扩展,True(允许)、False(不允许)
|
|
142
|
+
- anchor: 组件在父容器中的对齐方式,tk.N(北)、tk.S(南)、tk.E(东)、tk.W(西)、tk.CENTER(居中)
|
|
143
|
+
- padx: 组件在父容器中的水平间距,单位像素
|
|
144
|
+
- pady: 组件在父容器中的垂直间距,单位像素
|
|
145
|
+
:return: 新添加的按钮(Button)
|
|
146
|
+
:rtype: Button
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
kwargs['master'] = master if master else self.root
|
|
150
|
+
pack_kwargs = {
|
|
151
|
+
'side': kwargs.pop('side', tk.TOP),
|
|
152
|
+
'fill': kwargs.pop('fill', tk.NONE), # 按钮默认不填充父容器
|
|
153
|
+
'expand': kwargs.pop('expand', False if kwargs['master'] == self.root else True),
|
|
154
|
+
'anchor': kwargs.pop('anchor', tk.CENTER),
|
|
155
|
+
'padx': kwargs.pop('padx', 10), # 按钮默认水平间距10像素
|
|
156
|
+
'pady': kwargs.pop('pady', 10)} # 按钮默认垂直间距10像素
|
|
157
|
+
kwargs['master'] = master if master else self.root
|
|
158
|
+
|
|
159
|
+
# 创建组件
|
|
160
|
+
image = kwargs.pop('image', None)
|
|
161
|
+
button = tk.Button(**kwargs)
|
|
162
|
+
if image is not None: # 配置图片
|
|
163
|
+
self.config(button, image=image)
|
|
164
|
+
button.pack(pack_kwargs) # 放置组件
|
|
165
|
+
|
|
166
|
+
return button
|
|
167
|
+
|
|
168
|
+
def config(self, widget, **kwargs):
|
|
169
|
+
"""
|
|
170
|
+
1. 仅当传入image参数时,才处理图片转换和引用
|
|
171
|
+
2. 不传入image时,不修改image参数
|
|
172
|
+
"""
|
|
173
|
+
# 处理图片参数(仅当传入image时)
|
|
174
|
+
if 'image' in kwargs:
|
|
175
|
+
img = kwargs['image']
|
|
176
|
+
img_tk = self._convert_image(img)
|
|
177
|
+
widget.img_tk = img_tk # 关键:保留引用,避免垃圾回收
|
|
178
|
+
kwargs['image'] = img_tk
|
|
179
|
+
|
|
180
|
+
# 执行组件配置
|
|
181
|
+
widget.config(**kwargs)
|
|
182
|
+
|
|
183
|
+
def _convert_image(self, img):
|
|
184
|
+
"""
|
|
185
|
+
将不同格式的图片转换为tkinter可用的PhotoImage对象
|
|
186
|
+
支持:字符串路径、OpenCV图像(BGR格式)、PIL Image对象
|
|
187
|
+
"""
|
|
188
|
+
if isinstance(img, str) and img: # 字符串路径
|
|
189
|
+
# 检查文件是否存在
|
|
190
|
+
if not os.path.exists(img):
|
|
191
|
+
raise FileNotFoundError(f"图片文件不存在:{img}")
|
|
192
|
+
img_cv = cv2.imread(img)
|
|
193
|
+
img_rgb = cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB)
|
|
194
|
+
img_pil = Image.fromarray(img_rgb)
|
|
195
|
+
img_tk = ImageTk.PhotoImage(img_pil)
|
|
196
|
+
elif isinstance(img, np.ndarray): # OpenCV图像(BGR格式)
|
|
197
|
+
if len(img.shape) not in (2, 3):
|
|
198
|
+
raise ValueError("OpenCV图像维度错误,需为2D/3D数组")
|
|
199
|
+
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if len(img.shape) == 3 else img
|
|
200
|
+
img_pil = Image.fromarray(img_rgb)
|
|
201
|
+
img_tk = ImageTk.PhotoImage(img_pil)
|
|
202
|
+
elif isinstance(img, Image.Image): # PIL Image对象
|
|
203
|
+
img_tk = ImageTk.PhotoImage(img)
|
|
204
|
+
elif isinstance(img, ImageTk.PhotoImage):
|
|
205
|
+
img_tk = img
|
|
206
|
+
else: # 清空图片
|
|
207
|
+
img_tk = "" # 用空字符串替代None,避免tkinter报错
|
|
208
|
+
return img_tk
|
|
209
|
+
|
|
210
|
+
def after(self, delay, func):
|
|
211
|
+
self.root.after(delay, func)
|
|
212
|
+
|
|
213
|
+
def run(self):
|
|
214
|
+
self.root.mainloop()
|
|
215
|
+
|
|
216
|
+
def quit(self):
|
|
217
|
+
self.root.quit()
|
|
218
|
+
self.root.destroy()
|
|
219
|
+
|
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ezgo
|
|
3
|
+
Version: 0.0.7
|
|
4
|
+
Summary: 宇树Go2机器狗Python控制库
|
|
5
|
+
Author-email: ezgo <noreply@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/your-username/ezgo
|
|
8
|
+
Project-URL: Repository, https://github.com/your-username/ezgo
|
|
9
|
+
Project-URL: Bug Reports, https://github.com/your-username/ezgo/issues
|
|
10
|
+
Keywords: robotics,unitree,go2,robot,control
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.7
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: basic
|
|
26
|
+
Requires-Dist: opencv-python>=4.5.0; extra == "basic"
|
|
27
|
+
Requires-Dist: numpy>=1.19.0; extra == "basic"
|
|
28
|
+
Requires-Dist: Pillow>=8.0.0; extra == "basic"
|
|
29
|
+
Provides-Extra: full
|
|
30
|
+
Requires-Dist: opencv-python>=4.5.0; extra == "full"
|
|
31
|
+
Requires-Dist: numpy>=1.19.0; extra == "full"
|
|
32
|
+
Requires-Dist: Pillow>=8.0.0; extra == "full"
|
|
33
|
+
Requires-Dist: netifaces>=0.10.0; extra == "full"
|
|
34
|
+
Requires-Dist: unitree-sdk2py; extra == "full"
|
|
35
|
+
|
|
36
|
+
# ezgo - 宇树Go2机器狗Python控制库
|
|
37
|
+
|
|
38
|
+
[](https://badge.fury.io/py/ezgo)
|
|
39
|
+
[](https://pypi.org/project/ezgo/)
|
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
|
41
|
+
|
|
42
|
+
这是一个用于控制宇树Go2机器狗的Python库,提供了简单易用的API接口。支持运动控制、视频流获取、声光控制等功能。
|
|
43
|
+
|
|
44
|
+
## 功能特性
|
|
45
|
+
|
|
46
|
+
- 🚶 **基础运动控制**: 支持阻尼、站立、坐下、行走等基本动作
|
|
47
|
+
- 🏃 **高级运动模式**: 支持灵动、并腿跑、跳跃跑、交叉步等多种步态
|
|
48
|
+
- 🤸 **特技动作**: 支持空翻、倒立、舞蹈等表演动作
|
|
49
|
+
- 📷 **摄像头控制**: 支持图片获取和视频流处理
|
|
50
|
+
- 🔊 **声光控制**: 支持LED灯光和音量控制
|
|
51
|
+
- 🎮 **UI界面**: 提供图形化控制界面
|
|
52
|
+
- 🖼️ **EasyTk界面**: 简化的tkinter界面封装,适合教学使用
|
|
53
|
+
- 📹 **EzCamera摄像头**: 优化的摄像头控制,支持多平台和硬件加速
|
|
54
|
+
- 🔧 **易于使用**: 简洁的API设计,支持懒加载
|
|
55
|
+
- 📦 **可选依赖**: 核心功能轻量,按需安装依赖
|
|
56
|
+
|
|
57
|
+
## 安装
|
|
58
|
+
|
|
59
|
+
### 基础安装(仅核心库,无强制依赖)
|
|
60
|
+
```bash
|
|
61
|
+
pip install ezgo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 完整功能安装(包含所有可选依赖)
|
|
65
|
+
```bash
|
|
66
|
+
pip install ezgo[full]
|
|
67
|
+
n```
|
|
68
|
+
|
|
69
|
+
### 仅基础功能(图像处理相关依赖)
|
|
70
|
+
```bash
|
|
71
|
+
pip install ezgo[basic]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> **注意**: `ezgo` 核心库本身没有任何强制依赖。所有依赖都是可选的,您可以根据需要手动安装:
|
|
75
|
+
> - `unitree-sdk2py`: 机器人通信必需
|
|
76
|
+
> - `opencv-python`: 摄像头功能必需
|
|
77
|
+
> - `numpy`: 数值计算支持
|
|
78
|
+
> - `Pillow`: 图像处理支持
|
|
79
|
+
> - `netifaces`: �络接口检测
|
|
80
|
+
|
|
81
|
+
## 快速开始
|
|
82
|
+
|
|
83
|
+
### 基本运动控制
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import ezgo
|
|
87
|
+
|
|
88
|
+
# 创建Go2控制对象
|
|
89
|
+
robot = ezgo.Go2()
|
|
90
|
+
|
|
91
|
+
# 初始化连接
|
|
92
|
+
if robot.init():
|
|
93
|
+
print("连接成功!")
|
|
94
|
+
|
|
95
|
+
# 基本动作
|
|
96
|
+
robot.Damp() # 进入阻尼状态
|
|
97
|
+
robot.BalanceStand() # 平衡站立
|
|
98
|
+
robot.StopMove() # 停止移动
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 摄像头控制
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import ezgo
|
|
105
|
+
|
|
106
|
+
# 方法1: 通过Go2对象获取摄像头
|
|
107
|
+
robot = ezgo.Go2()
|
|
108
|
+
camera = robot.get_camera()
|
|
109
|
+
|
|
110
|
+
# 方法2: 直接使用摄像头类
|
|
111
|
+
camera = ezgo.Go2Camera()
|
|
112
|
+
|
|
113
|
+
# 初始化摄像头
|
|
114
|
+
if camera.init():
|
|
115
|
+
# 获取单张图片
|
|
116
|
+
image = camera.capture_image("photo.jpg")
|
|
117
|
+
|
|
118
|
+
# 打开视频流
|
|
119
|
+
if camera.open_video_stream():
|
|
120
|
+
frame = camera.read_frame()
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 声光控制
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import ezgo
|
|
127
|
+
|
|
128
|
+
# 方法1: 通过Go2对象获取VUI控制
|
|
129
|
+
robot = ezgo.Go2()
|
|
130
|
+
vui = robot.get_vui()
|
|
131
|
+
|
|
132
|
+
# 方法2: 直接使用VUI类
|
|
133
|
+
vui = ezgo.Go2VUI()
|
|
134
|
+
|
|
135
|
+
# 初始化VUI
|
|
136
|
+
if vui.init():
|
|
137
|
+
# 设置LED亮度 (0-10)
|
|
138
|
+
vui.set_brightness(5)
|
|
139
|
+
|
|
140
|
+
# 获取当前亮度
|
|
141
|
+
success, brightness = vui.get_brightness()
|
|
142
|
+
|
|
143
|
+
# 设置音量 (0-10)
|
|
144
|
+
vui.set_volume(3)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 高级运动模式
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
import ezgo
|
|
151
|
+
|
|
152
|
+
robot = ezgo.Go2()
|
|
153
|
+
if robot.init():
|
|
154
|
+
# 步态模式切换
|
|
155
|
+
robot.FreeWalk(True) # 开启灵动模式
|
|
156
|
+
robot.FreeWalk(False) # 关闭灵动模式
|
|
157
|
+
|
|
158
|
+
# 特殊步态(自动演示后关闭)
|
|
159
|
+
robot.FreeBound(True) # 并腿跑模式(2秒后自动关闭)
|
|
160
|
+
robot.FreeJump(True) # 跳跃模式(4秒后自动关闭)
|
|
161
|
+
robot.FreeAvoid(True) # 闪避模式(2秒后自动关闭)
|
|
162
|
+
robot.WalkUpright(True) # 后腿直立模式(4秒后自动关闭)
|
|
163
|
+
robot.CrossStep(True) # 交叉步模式(4秒后自动关闭)
|
|
164
|
+
|
|
165
|
+
# 常规运动模式
|
|
166
|
+
robot.TrotRun() # 进入常规跑步模式
|
|
167
|
+
robot.StaticWalk() # 进入常规行走模式
|
|
168
|
+
robot.EconomicGait() # 进入常规续航模式
|
|
169
|
+
robot.ClassicWalk(True) # 开启经典步态
|
|
170
|
+
robot.ClassicWalk(False) # 关闭经典步态
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 特技动作
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import ezgo
|
|
177
|
+
|
|
178
|
+
robot = ezgo.Go2()
|
|
179
|
+
if robot.init():
|
|
180
|
+
# 表演动作
|
|
181
|
+
robot.Hello() # 打招呼
|
|
182
|
+
robot.Stretch() # 伸懒腰
|
|
183
|
+
robot.Content() # 开心
|
|
184
|
+
robot.Heart() # 比心
|
|
185
|
+
robot.Scrape() # 拜年作揖
|
|
186
|
+
robot.Dance1() # 舞蹈段落1
|
|
187
|
+
robot.Dance2() # 舞蹈段落2
|
|
188
|
+
|
|
189
|
+
# 特技动作
|
|
190
|
+
robot.FrontJump() # 前跳
|
|
191
|
+
robot.FrontPounce() # 向前扑人
|
|
192
|
+
robot.LeftFlip() # 左空翻
|
|
193
|
+
robot.BackFlip() # 后空翻
|
|
194
|
+
robot.HandStand(True) # 倒立(4秒后自动关闭)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 自动翻身功能
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import ezgo
|
|
201
|
+
|
|
202
|
+
robot = ezgo.Go2()
|
|
203
|
+
if robot.init():
|
|
204
|
+
# 设置自动翻身
|
|
205
|
+
success = robot.AutoRecoverSet(True)
|
|
206
|
+
if success:
|
|
207
|
+
print("自动翻身已开启")
|
|
208
|
+
|
|
209
|
+
# 查询自动翻身状态
|
|
210
|
+
is_enabled = robot.AutoRecoverGet()
|
|
211
|
+
print(f"自动翻身状态: {'开启' if is_enabled else '关闭'}")
|
|
212
|
+
|
|
213
|
+
# 关闭自动翻身
|
|
214
|
+
robot.AutoRecoverSet(False)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### EasyTk 简化界面开发
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
import ezgo
|
|
221
|
+
|
|
222
|
+
# 创建EasyTk窗口(自动适配灵芯派480x800分辨率)
|
|
223
|
+
app = ezgo.EasyTk(title="机器狗控制", size="480x800")
|
|
224
|
+
|
|
225
|
+
# 添加标签
|
|
226
|
+
label = app.add_label(text="欢迎使用ezgo", font=("SimHei", 24))
|
|
227
|
+
|
|
228
|
+
# 添加按钮
|
|
229
|
+
def on_click():
|
|
230
|
+
print("按钮被点击了!")
|
|
231
|
+
|
|
232
|
+
button = app.add_button(text="点击我", command=on_click)
|
|
233
|
+
|
|
234
|
+
# 添加图片显示(支持文件路径、OpenCV图像、PIL图像)
|
|
235
|
+
image_label = app.add_label(image="path/to/image.jpg")
|
|
236
|
+
|
|
237
|
+
# 运行主循环
|
|
238
|
+
app.root.mainloop()
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### EzCamera 优化摄像头控制
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
import ezgo
|
|
245
|
+
import cv2
|
|
246
|
+
|
|
247
|
+
# 创建摄像头对象(自动适配平台)
|
|
248
|
+
camera = ezgo.EzCamera(index=0, width=640, height=480, fps=30)
|
|
249
|
+
|
|
250
|
+
# 打开摄像头
|
|
251
|
+
if camera.open():
|
|
252
|
+
print("摄像头打开成功!")
|
|
253
|
+
|
|
254
|
+
# 读取最新帧
|
|
255
|
+
frame = camera.read()
|
|
256
|
+
if frame is not None:
|
|
257
|
+
# 保存图片
|
|
258
|
+
camera.save_image(frame, "capture.jpg")
|
|
259
|
+
|
|
260
|
+
# 调整尺寸
|
|
261
|
+
resized = camera.resize(frame, (224, 224))
|
|
262
|
+
|
|
263
|
+
# 裁剪为正方形
|
|
264
|
+
square = camera.crop_to_square(frame)
|
|
265
|
+
|
|
266
|
+
# 显示图像
|
|
267
|
+
cv2.imshow("Camera", frame)
|
|
268
|
+
cv2.waitKey(0)
|
|
269
|
+
cv2.destroyAllWindows()
|
|
270
|
+
|
|
271
|
+
# 关闭摄像头
|
|
272
|
+
camera.close()
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## API 参考
|
|
276
|
+
|
|
277
|
+
### Go2 类
|
|
278
|
+
|
|
279
|
+
主要的机器狗控制类,提供运动控制和状态管理功能。
|
|
280
|
+
|
|
281
|
+
#### 主要方法
|
|
282
|
+
|
|
283
|
+
**基础控制**:
|
|
284
|
+
- `init()`: 初始化与Go2的连接
|
|
285
|
+
- `Damp()`: 进入阻尼状态
|
|
286
|
+
- `BalanceStand()`: 平衡站立
|
|
287
|
+
- `StopMove()`: 停止所有移动动作
|
|
288
|
+
- `StandUp()`: 关节锁定,站高
|
|
289
|
+
- `StandDown()`: 关节锁定,站低
|
|
290
|
+
- `Sit()`: 坐下
|
|
291
|
+
- `RiseSit()`: 站起(相对于坐下)
|
|
292
|
+
|
|
293
|
+
**移动控制**:
|
|
294
|
+
- `Move(vx, vy, vyaw)`: 移动(速度范围:vx, vy: -1~1, vyaw: -2~2)
|
|
295
|
+
- `MoveForDuration(vx, vy, vyaw, duration)`: 持续移动指定时间
|
|
296
|
+
- `StartMove(vx, vy, vyaw)`: 开始持续移动
|
|
297
|
+
- `Forward(speed=0.3, duration=2.0)`: 向前移动
|
|
298
|
+
- `Backward(speed=0.3, duration=2.0)`: 向后移动
|
|
299
|
+
- `Left(speed=0.3, duration=2.0)`: 向左移动
|
|
300
|
+
- `Right(speed=0.3, duration=2.0)`: 向右移动
|
|
301
|
+
- `TurnLeft(speed=0.5, duration=2.0)`: 左转
|
|
302
|
+
- `TurnRight(speed=0.5, duration=2.0)`: 右转
|
|
303
|
+
|
|
304
|
+
**步态模式**:
|
|
305
|
+
- `FreeWalk(flag=None)`: 灵动模式(默认步态)
|
|
306
|
+
- `FreeBound(flag)`: 并腿跑模式
|
|
307
|
+
- `FreeJump(flag)`: 跳跃模式
|
|
308
|
+
- `FreeAvoid(flag)`: 闪避模式
|
|
309
|
+
- `WalkUpright(flag)`: 后腿直立模式
|
|
310
|
+
- `CrossStep(flag)`: 交叉步模式
|
|
311
|
+
- `ClassicWalk(flag)`: 经典步态
|
|
312
|
+
- `TrotRun()`: 进入常规跑步模式
|
|
313
|
+
- `StaticWalk()`: 进入常规行走模式
|
|
314
|
+
- `EconomicGait()`: 进入常规续航模式
|
|
315
|
+
|
|
316
|
+
**表演动作**:
|
|
317
|
+
- `Hello()`: 打招呼
|
|
318
|
+
- `Stretch()`: 伸懒腰
|
|
319
|
+
- `Content()`: 开心
|
|
320
|
+
- `Heart()`: 比心
|
|
321
|
+
- `Scrape()`: 拜年作揖
|
|
322
|
+
- `Dance1()`: 舞蹈段落1
|
|
323
|
+
- `Dance2()`: 舞蹈段落2
|
|
324
|
+
|
|
325
|
+
**特技动作**:
|
|
326
|
+
- `FrontJump()`: 前跳
|
|
327
|
+
- `FrontPounce()`: 向前扑人
|
|
328
|
+
- `LeftFlip()`: 左空翻
|
|
329
|
+
- `BackFlip()`: 后空翻
|
|
330
|
+
- `HandStand(flag)`: 倒立行走
|
|
331
|
+
|
|
332
|
+
**自动功能**:
|
|
333
|
+
- `AutoRecoverSet(flag)`: 设置自动翻身是否生效
|
|
334
|
+
- `AutoRecoverGet()`: 查询自动翻身是否生效
|
|
335
|
+
- `SwitchAvoidMode()`: 闪避模式下,关闭摇杆未推时前方障碍物的闪避
|
|
336
|
+
|
|
337
|
+
**便利方法**:
|
|
338
|
+
- `get_camera()`: 获取摄像头控制对象
|
|
339
|
+
- `get_vui()`: 获取声光控制对象
|
|
340
|
+
- `capture_image(save_path=None)`: 获取一张图片
|
|
341
|
+
- `start_video_stream(width=480, height=320)`: 开始视频流
|
|
342
|
+
- `get_video_frame()`: 获取最新视频帧
|
|
343
|
+
- `stop_video_stream()`: 停止视频流
|
|
344
|
+
- `cleanup()`: 清理资源
|
|
345
|
+
|
|
346
|
+
### Go2Camera 类
|
|
347
|
+
|
|
348
|
+
摄像头控制类,提供图片获取和视频流功能。
|
|
349
|
+
|
|
350
|
+
#### 主要方法
|
|
351
|
+
|
|
352
|
+
- `init()`: 初始化摄像头连接
|
|
353
|
+
- `capture_image(save_path=None)`: 获取单张图片
|
|
354
|
+
- `open_video_stream(width=480, height=320)`: 打开视频流
|
|
355
|
+
- `read_frame()`: 从视频流读取一帧
|
|
356
|
+
- `start_stream(width=480, height=320)`: 开始后台视频流
|
|
357
|
+
|
|
358
|
+
### Go2VUI 类
|
|
359
|
+
|
|
360
|
+
声光控制类,提供LED灯光和音量控制功能。
|
|
361
|
+
|
|
362
|
+
#### 主要方法
|
|
363
|
+
|
|
364
|
+
- `init()`: 初始化VUI客户端
|
|
365
|
+
- `set_brightness(level)`: 设置LED亮度 (0-10)
|
|
366
|
+
- `get_brightness()`: 获取当前LED亮度
|
|
367
|
+
- `set_volume(level)`: 设置音量 (0-10)
|
|
368
|
+
- `get_volume()`: 获取当前音量
|
|
369
|
+
|
|
370
|
+
### EasyTk 类
|
|
371
|
+
|
|
372
|
+
简化的tkinter界面封装,专为教学和快速开发设计。
|
|
373
|
+
|
|
374
|
+
#### 主要方法
|
|
375
|
+
|
|
376
|
+
- `__init__(title="APP", size=None)`: 初始化窗口,自动适配灵芯派
|
|
377
|
+
- `add_frame(master=None, **kwargs)`: 添加框架容器
|
|
378
|
+
- `add_label(master=None, **kwargs)`: 添加标签,支持文本和图片
|
|
379
|
+
- `add_button(master=None, **kwargs)`: 添加按钮
|
|
380
|
+
- `config(widget, **kwargs)`: 配置组件属性
|
|
381
|
+
|
|
382
|
+
#### 特性
|
|
383
|
+
|
|
384
|
+
- **自动适配**: 在Linux系统下自动设置480x800分辨率和全屏模式
|
|
385
|
+
- **简化布局**: 使用pack布局,简化界面开发
|
|
386
|
+
- **图片支持**: 直接支持文件路径、OpenCV图像、PIL图像
|
|
387
|
+
- **教学友好**: 适合学生快速上手GUI开发
|
|
388
|
+
|
|
389
|
+
### EzCamera 类
|
|
390
|
+
|
|
391
|
+
优化的摄像头控制类,支持多平台和硬件加速。
|
|
392
|
+
|
|
393
|
+
#### 主要方法
|
|
394
|
+
|
|
395
|
+
- `__init__(index=0, width=640, height=480, fps=30)`: 初始化摄像头
|
|
396
|
+
- `open(timeout=5)`: 打开摄像头,支持硬件加速
|
|
397
|
+
- `read()`: 读取最新帧(非阻塞)
|
|
398
|
+
- `save_image(frame, filename="frame.jpg")`: 保存图像
|
|
399
|
+
- `resize(frame, size=(224, 224))`: 调整图像尺寸
|
|
400
|
+
- `crop_to_square(frame)`: 裁剪为正方形
|
|
401
|
+
- `close()`: 关闭摄像头
|
|
402
|
+
|
|
403
|
+
#### 特性
|
|
404
|
+
|
|
405
|
+
- **多平台支持**: Linux(V4L2)、Windows(DSHOW)、macOS自动适配
|
|
406
|
+
- **硬件加速**: 支持MJPG格式和缓冲区优化
|
|
407
|
+
- **双缓冲**: 使用双缓冲机制避免图像撕裂
|
|
408
|
+
- **线程安全**: 独立读取线程,主线程非阻塞
|
|
409
|
+
- **帧率控制**: 精确的帧率控制,避免累积误差
|
|
410
|
+
|
|
411
|
+
## 依赖要求
|
|
412
|
+
|
|
413
|
+
### 核心依赖
|
|
414
|
+
- Python >= 3.7
|
|
415
|
+
|
|
416
|
+
### 可选依赖
|
|
417
|
+
|
|
418
|
+
**基础功能**:
|
|
419
|
+
- opencv-python >= 4.5.0
|
|
420
|
+
- numpy >= 1.19.0
|
|
421
|
+
- Pillow >= 8.0.0
|
|
422
|
+
|
|
423
|
+
**完整功能**:
|
|
424
|
+
- netifaces >= 0.10.0
|
|
425
|
+
- unitree-sdk2py (需要从官方源安装)
|
|
426
|
+
|
|
427
|
+
## 注意事项
|
|
428
|
+
|
|
429
|
+
1. **网络连接**: 确保计算机与Go2机器狗在同一网络中
|
|
430
|
+
2. **权限要求**: 某些功能可能需要管理员权限
|
|
431
|
+
3. **依赖安装**: `unitree-sdk2py` 需要从宇树官方源安装
|
|
432
|
+
4. **接口检测**: 库会自动检测网络接口,也可手动指定
|
|
433
|
+
|
|
434
|
+
## 故障排除
|
|
435
|
+
|
|
436
|
+
### 常见问题
|
|
437
|
+
|
|
438
|
+
1. **导入错误**: 确保已安装所有必需的依赖包
|
|
439
|
+
2. **连接失败**: 检查网络连接和防火墙设置
|
|
440
|
+
3. **摄像头问题**: 确保GStreamer正确安装
|
|
441
|
+
4. **权限问题**: 在某些系统上可能需要管理员权限
|
|
442
|
+
|
|
443
|
+
### 依赖安装
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
# 安装基础依赖
|
|
447
|
+
pip install opencv-python numpy Pillow netifaces
|
|
448
|
+
|
|
449
|
+
# unitree-sdk2py 需要从官方源安装
|
|
450
|
+
# 请参考宇树官方文档
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## 许可证
|
|
454
|
+
|
|
455
|
+
本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。
|
|
456
|
+
|
|
457
|
+
## 贡献
|
|
458
|
+
|
|
459
|
+
欢迎提交 Issue 和 Pull Request!
|
|
460
|
+
|
|
461
|
+
## 更新日志
|
|
462
|
+
|
|
463
|
+
### v0.0.7 (2025-12-09)
|
|
464
|
+
**新增功能**:
|
|
465
|
+
- ✨ 新增EasyTk简化界面封装类,专为教学设计
|
|
466
|
+
- ✨ 新增EzCamera优化摄像头控制类,支持多平台硬件加速
|
|
467
|
+
- ✨ EasyTk自动适配灵芯派480x800分辨率和全屏模式
|
|
468
|
+
- ✨ EzCamera支持双缓冲机制和线程安全读取
|
|
469
|
+
- ✨ 完善图片处理功能:调整尺寸、裁剪正方形、保存图像
|
|
470
|
+
|
|
471
|
+
**改进**:
|
|
472
|
+
- 🔧 优化模块导入结构,新增EasyTk和EzCamera到公开API
|
|
473
|
+
- 🔧 增强平台兼容性,自动检测并适配不同操作系统
|
|
474
|
+
- 🔧 完善文档,添加详细的使用示例和API说明
|
|
475
|
+
- 🔧 改进错误处理,提供更友好的错误提示
|
|
476
|
+
|
|
477
|
+
**特性**:
|
|
478
|
+
- 🖼️ EasyTk支持直接显示文件路径、OpenCV图像、PIL图像
|
|
479
|
+
- 📹 EzCamera支持MJPG硬件加速和V4L2优化
|
|
480
|
+
- 🎯 教学友好,简化GUI开发和摄像头操作流程
|
|
481
|
+
|
|
482
|
+
### v0.0.5 (2025-12-04)
|
|
483
|
+
**新增功能**:
|
|
484
|
+
- ✨ 新增15种高级运动模式和特技动作
|
|
485
|
+
- ✨ 支持灵动模式、并腿跑、跳跃跑、交叉步等多种步态
|
|
486
|
+
- ✨ 新增空翻、倒立、舞蹈等表演动作
|
|
487
|
+
- ✨ 增加自动翻身功能设置和查询
|
|
488
|
+
- ✨ 完善移动控制API,支持持续移动和定时移动
|
|
489
|
+
|
|
490
|
+
**改进**:
|
|
491
|
+
- 🔧 优化网络连接检查,支持非sudo ping
|
|
492
|
+
- 🔧 改进初始化流程,连接失败时仅显示警告
|
|
493
|
+
- 🔧 增强错误处理,对不支持的SDK版本提供友好提示
|
|
494
|
+
- 🔧 所有新函数都包含适当的异常处理
|
|
495
|
+
|
|
496
|
+
**修复**:
|
|
497
|
+
- 🐛 修复包构建问题,确保源代码正确包含
|
|
498
|
+
- 🐛 修复依赖配置,核心库无强制依赖
|
|
499
|
+
|
|
500
|
+
### v0.0.4 (2025-12-04)
|
|
501
|
+
**修复**:
|
|
502
|
+
- 🐛 修复包结构问题,源代码未正确包含在PyPI包中
|
|
503
|
+
- 🔧 重新组织包结构,确保所有模块正确打包
|
|
504
|
+
|
|
505
|
+
### v0.0.3 (2025-12-04)
|
|
506
|
+
**改进**:
|
|
507
|
+
- 🔧 优化依赖配置,移除强制依赖
|
|
508
|
+
- 📦 所有依赖改为可选依赖,支持按需安装
|
|
509
|
+
- 📚 更新安装说明和依赖文档
|
|
510
|
+
|
|
511
|
+
### v0.0.2 (2025-12-04)
|
|
512
|
+
**新增功能**:
|
|
513
|
+
- ✨ 集成go2_vui声光控制模块
|
|
514
|
+
- ✨ 集成go2_camera摄像头控制模块
|
|
515
|
+
- ✨ 优化模块导入结构,使用相对导入
|
|
516
|
+
|
|
517
|
+
**改进**:
|
|
518
|
+
- 🔧 修复get_camera方法路径配置
|
|
519
|
+
- 🔧 添加get_vui方法支持声光控制
|
|
520
|
+
- 🔧 完善懒加载机制
|
|
521
|
+
|
|
522
|
+
### v0.0.1 (2025-12-04)
|
|
523
|
+
**初始发布**:
|
|
524
|
+
- 🎉 基础运动控制功能
|
|
525
|
+
- 📷 摄像头基础支持
|
|
526
|
+
- 🔊 声光控制基础支持
|
|
527
|
+
- 📦 PyPI包发布
|
|
528
|
+
|
|
529
|
+
### v0.0.2
|
|
530
|
+
- 添加了 Go2Camera 摄像头控制类
|
|
531
|
+
- 添加了 Go2VUI 声光控制类
|
|
532
|
+
- 优化了导入和错误处理
|
|
533
|
+
- 改进了文档和示例
|
|
534
|
+
|
|
535
|
+
### v0.0.1
|
|
536
|
+
- 初始版本发布
|
|
537
|
+
- 基本运动控制功能
|
|
538
|
+
- UI界面支持
|
|
539
|
+
|
|
540
|
+
## 链接
|
|
541
|
+
|
|
542
|
+
- [PyPI 项目页面](https://pypi.org/project/ezgo/)
|
|
543
|
+
- [GitHub 仓库](https://github.com/your-username/ezgo)
|
|
544
|
+
- [问题反馈](https://github.com/your-username/ezgo/issues)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ezgo/__init__.py,sha256=oRflrSHwO_CTnNJ2ZvhNmW-ezZP1hh9RtJWOx7bkjIo,2120
|
|
2
|
+
ezgo/camera.py,sha256=Vsr44vFtaop4LE0DlWyOHyvxF-jHnVO8iMPUwp5mbsE,2538
|
|
3
|
+
ezgo/ezcamera.py,sha256=6-3y-1X_L60QsWdsd5-caLoj7Quy9rpyHdp3Tq3JFbU,6536
|
|
4
|
+
ezgo/eztk.py,sha256=MHWkkZYIL1AFjUXFu2Qq8U9idJV_E1VxFUBe82dvbyA,10463
|
|
5
|
+
ezgo/go2.py,sha256=lSZwpYRAljcV4bAalkZXvs9X-qMaEGcuaHSNegGcX6M,31610
|
|
6
|
+
ezgo/go2_camera.py,sha256=nawUSLyvqTNiVRQ2sM-UdoJzqwZcySltLbCfUb0G9Qc,8614
|
|
7
|
+
ezgo/go2_vui.py,sha256=52I4Y8uqGkW9muDEnDzQ236HqVVTIgNIN_2gW5RTC48,10161
|
|
8
|
+
ezgo/ui.py,sha256=jUik6maaoemI2vsBM92OLZfJfskg54W7hwMls_gxppg,2777
|
|
9
|
+
ezgo-0.0.7.dist-info/LICENSE,sha256=Zk4eZBT3KaBhqM3LB_xN7QquwnoWsEHAoA6eLdE6W5M,1060
|
|
10
|
+
ezgo-0.0.7.dist-info/METADATA,sha256=dshHGchZ_YsggtBJPzcXzDNKaIgJysgOWMlizX7Hr_4,16626
|
|
11
|
+
ezgo-0.0.7.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
12
|
+
ezgo-0.0.7.dist-info/top_level.txt,sha256=BdCFEVD5V_4FxUtvH0BVlZKgxYnp7EKNsYs5OyFxj-g,5
|
|
13
|
+
ezgo-0.0.7.dist-info/RECORD,,
|
ezgo-0.0.5.dist-info/METADATA
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: ezgo
|
|
3
|
-
Version: 0.0.5
|
|
4
|
-
Summary: 宇树Go2机器狗Python控制库
|
|
5
|
-
Author-email: ezgo <noreply@example.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/your-username/ezgo
|
|
8
|
-
Project-URL: Repository, https://github.com/your-username/ezgo
|
|
9
|
-
Project-URL: Bug Reports, https://github.com/your-username/ezgo/issues
|
|
10
|
-
Keywords: robotics,unitree,go2,robot,control
|
|
11
|
-
Classifier: Development Status :: 3 - Alpha
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Requires-Python: >=3.7
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
License-File: LICENSE
|
|
25
|
-
Provides-Extra: basic
|
|
26
|
-
Requires-Dist: opencv-python>=4.5.0; extra == "basic"
|
|
27
|
-
Requires-Dist: numpy>=1.19.0; extra == "basic"
|
|
28
|
-
Requires-Dist: Pillow>=8.0.0; extra == "basic"
|
|
29
|
-
Provides-Extra: full
|
|
30
|
-
Requires-Dist: opencv-python>=4.5.0; extra == "full"
|
|
31
|
-
Requires-Dist: numpy>=1.19.0; extra == "full"
|
|
32
|
-
Requires-Dist: Pillow>=8.0.0; extra == "full"
|
|
33
|
-
Requires-Dist: netifaces>=0.10.0; extra == "full"
|
|
34
|
-
Requires-Dist: unitree-sdk2py; extra == "full"
|
|
35
|
-
|
|
36
|
-
# ezgo - 宇树Go2机器狗Python控制库
|
|
37
|
-
|
|
38
|
-
[](https://badge.fury.io/py/ezgo)
|
|
39
|
-
[](https://pypi.org/project/ezgo/)
|
|
40
|
-
[](https://opensource.org/licenses/MIT)
|
|
41
|
-
|
|
42
|
-
这是一个用于控制宇树Go2机器狗的Python库,提供了简单易用的API接口。支持运动控制、视频流获取、声光控制等功能。
|
|
43
|
-
|
|
44
|
-
## 功能特性
|
|
45
|
-
|
|
46
|
-
- 🚶 **运动控制**: 支持行走、跑步、跳跃等多种运动模式
|
|
47
|
-
- 📷 **摄像头控制**: 支持图片获取和视频流处理
|
|
48
|
-
- 🔊 **声光控制**: 支持LED灯光和音量控制
|
|
49
|
-
- 🎮 **UI界面**: 提供图形化控制界面
|
|
50
|
-
- 🔧 **易于使用**: 简洁的API设计,支持懒加载
|
|
51
|
-
- 📦 **可选依赖**: 核心功能轻量,按需安装依赖
|
|
52
|
-
|
|
53
|
-
## 安装
|
|
54
|
-
|
|
55
|
-
### 基础安装(仅核心库,无强制依赖)
|
|
56
|
-
```bash
|
|
57
|
-
pip install ezgo
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### 完整功能安装(包含所有可选依赖)
|
|
61
|
-
```bash
|
|
62
|
-
pip install ezgo[full]
|
|
63
|
-
n```
|
|
64
|
-
|
|
65
|
-
### 仅基础功能(图像处理相关依赖)
|
|
66
|
-
```bash
|
|
67
|
-
pip install ezgo[basic]
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
> **注意**: `ezgo` 核心库本身没有任何强制依赖。所有依赖都是可选的,您可以根据需要手动安装:
|
|
71
|
-
> - `unitree-sdk2py`: 机器人通信必需
|
|
72
|
-
> - `opencv-python`: 摄像头功能必需
|
|
73
|
-
> - `numpy`: 数值计算支持
|
|
74
|
-
> - `Pillow`: 图像处理支持
|
|
75
|
-
> - `netifaces`: �络接口检测
|
|
76
|
-
|
|
77
|
-
## 快速开始
|
|
78
|
-
|
|
79
|
-
### 基本运动控制
|
|
80
|
-
|
|
81
|
-
```python
|
|
82
|
-
import ezgo
|
|
83
|
-
|
|
84
|
-
# 创建Go2控制对象
|
|
85
|
-
robot = ezgo.Go2()
|
|
86
|
-
|
|
87
|
-
# 初始化连接
|
|
88
|
-
if robot.init():
|
|
89
|
-
print("连接成功!")
|
|
90
|
-
|
|
91
|
-
# 基本动作
|
|
92
|
-
robot.Damp() # 进入阻尼状态
|
|
93
|
-
robot.BalanceStand() # 平衡站立
|
|
94
|
-
robot.StopMove() # 停止移动
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 摄像头控制
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
import ezgo
|
|
101
|
-
|
|
102
|
-
# 方法1: 通过Go2对象获取摄像头
|
|
103
|
-
robot = ezgo.Go2()
|
|
104
|
-
camera = robot.get_camera()
|
|
105
|
-
|
|
106
|
-
# 方法2: 直接使用摄像头类
|
|
107
|
-
camera = ezgo.Go2Camera()
|
|
108
|
-
|
|
109
|
-
# 初始化摄像头
|
|
110
|
-
if camera.init():
|
|
111
|
-
# 获取单张图片
|
|
112
|
-
image = camera.capture_image("photo.jpg")
|
|
113
|
-
|
|
114
|
-
# 打开视频流
|
|
115
|
-
if camera.open_video_stream():
|
|
116
|
-
frame = camera.read_frame()
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### 声光控制
|
|
120
|
-
|
|
121
|
-
```python
|
|
122
|
-
import ezgo
|
|
123
|
-
|
|
124
|
-
# 方法1: 通过Go2对象获取VUI控制
|
|
125
|
-
robot = ezgo.Go2()
|
|
126
|
-
vui = robot.get_vui()
|
|
127
|
-
|
|
128
|
-
# 方法2: 直接使用VUI类
|
|
129
|
-
vui = ezgo.Go2VUI()
|
|
130
|
-
|
|
131
|
-
# 初始化VUI
|
|
132
|
-
if vui.init():
|
|
133
|
-
# 设置LED亮度 (0-10)
|
|
134
|
-
vui.set_brightness(5)
|
|
135
|
-
|
|
136
|
-
# 获取当前亮度
|
|
137
|
-
success, brightness = vui.get_brightness()
|
|
138
|
-
|
|
139
|
-
# 设置音量 (0-10)
|
|
140
|
-
vui.set_volume(3)
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## API 参考
|
|
144
|
-
|
|
145
|
-
### Go2 类
|
|
146
|
-
|
|
147
|
-
主要的机器狗控制类,提供运动控制和状态管理功能。
|
|
148
|
-
|
|
149
|
-
#### 主要方法
|
|
150
|
-
|
|
151
|
-
- `init()`: 初始化与Go2的连接
|
|
152
|
-
- `Damp()`: 进入阻尼状态
|
|
153
|
-
- `BalanceStand()`: 平衡站立
|
|
154
|
-
- `StopMove()`: 停止所有移动动作
|
|
155
|
-
- `get_camera()`: 获取摄像头控制对象
|
|
156
|
-
- `get_vui()`: 获取声光控制对象
|
|
157
|
-
|
|
158
|
-
### Go2Camera 类
|
|
159
|
-
|
|
160
|
-
摄像头控制类,提供图片获取和视频流功能。
|
|
161
|
-
|
|
162
|
-
#### 主要方法
|
|
163
|
-
|
|
164
|
-
- `init()`: 初始化摄像头连接
|
|
165
|
-
- `capture_image(save_path=None)`: 获取单张图片
|
|
166
|
-
- `open_video_stream(width=480, height=320)`: 打开视频流
|
|
167
|
-
- `read_frame()`: 从视频流读取一帧
|
|
168
|
-
- `start_stream(width=480, height=320)`: 开始后台视频流
|
|
169
|
-
|
|
170
|
-
### Go2VUI 类
|
|
171
|
-
|
|
172
|
-
声光控制类,提供LED灯光和音量控制功能。
|
|
173
|
-
|
|
174
|
-
#### 主要方法
|
|
175
|
-
|
|
176
|
-
- `init()`: 初始化VUI客户端
|
|
177
|
-
- `set_brightness(level)`: 设置LED亮度 (0-10)
|
|
178
|
-
- `get_brightness()`: 获取当前LED亮度
|
|
179
|
-
- `set_volume(level)`: 设置音量 (0-10)
|
|
180
|
-
- `get_volume()`: 获取当前音量
|
|
181
|
-
|
|
182
|
-
## 依赖要求
|
|
183
|
-
|
|
184
|
-
### 核心依赖
|
|
185
|
-
- Python >= 3.7
|
|
186
|
-
|
|
187
|
-
### 可选依赖
|
|
188
|
-
|
|
189
|
-
**基础功能**:
|
|
190
|
-
- opencv-python >= 4.5.0
|
|
191
|
-
- numpy >= 1.19.0
|
|
192
|
-
- Pillow >= 8.0.0
|
|
193
|
-
|
|
194
|
-
**完整功能**:
|
|
195
|
-
- netifaces >= 0.10.0
|
|
196
|
-
- unitree-sdk2py (需要从官方源安装)
|
|
197
|
-
|
|
198
|
-
## 注意事项
|
|
199
|
-
|
|
200
|
-
1. **网络连接**: 确保计算机与Go2机器狗在同一网络中
|
|
201
|
-
2. **权限要求**: 某些功能可能需要管理员权限
|
|
202
|
-
3. **依赖安装**: `unitree-sdk2py` 需要从宇树官方源安装
|
|
203
|
-
4. **接口检测**: 库会自动检测网络接口,也可手动指定
|
|
204
|
-
|
|
205
|
-
## 故障排除
|
|
206
|
-
|
|
207
|
-
### 常见问题
|
|
208
|
-
|
|
209
|
-
1. **导入错误**: 确保已安装所有必需的依赖包
|
|
210
|
-
2. **连接失败**: 检查网络连接和防火墙设置
|
|
211
|
-
3. **摄像头问题**: 确保GStreamer正确安装
|
|
212
|
-
4. **权限问题**: 在某些系统上可能需要管理员权限
|
|
213
|
-
|
|
214
|
-
### 依赖安装
|
|
215
|
-
|
|
216
|
-
```bash
|
|
217
|
-
# 安装基础依赖
|
|
218
|
-
pip install opencv-python numpy Pillow netifaces
|
|
219
|
-
|
|
220
|
-
# unitree-sdk2py 需要从官方源安装
|
|
221
|
-
# 请参考宇树官方文档
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## 许可证
|
|
225
|
-
|
|
226
|
-
本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。
|
|
227
|
-
|
|
228
|
-
## 贡献
|
|
229
|
-
|
|
230
|
-
欢迎提交 Issue 和 Pull Request!
|
|
231
|
-
|
|
232
|
-
## 更新日志
|
|
233
|
-
|
|
234
|
-
### v0.0.2
|
|
235
|
-
- 添加了 Go2Camera 摄像头控制类
|
|
236
|
-
- 添加了 Go2VUI 声光控制类
|
|
237
|
-
- 优化了导入和错误处理
|
|
238
|
-
- 改进了文档和示例
|
|
239
|
-
|
|
240
|
-
### v0.0.1
|
|
241
|
-
- 初始版本发布
|
|
242
|
-
- 基本运动控制功能
|
|
243
|
-
- UI界面支持
|
|
244
|
-
|
|
245
|
-
## 链接
|
|
246
|
-
|
|
247
|
-
- [PyPI 项目页面](https://pypi.org/project/ezgo/)
|
|
248
|
-
- [GitHub 仓库](https://github.com/your-username/ezgo)
|
|
249
|
-
- [问题反馈](https://github.com/your-username/ezgo/issues)
|
ezgo-0.0.5.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
ezgo/__init__.py,sha256=6OsmnWvKm2s_BqjwPGTqeYpYt4XUybSAOnvOdDJzCLw,1978
|
|
2
|
-
ezgo/camera.py,sha256=Vsr44vFtaop4LE0DlWyOHyvxF-jHnVO8iMPUwp5mbsE,2538
|
|
3
|
-
ezgo/go2.py,sha256=lSZwpYRAljcV4bAalkZXvs9X-qMaEGcuaHSNegGcX6M,31610
|
|
4
|
-
ezgo/go2_camera.py,sha256=nawUSLyvqTNiVRQ2sM-UdoJzqwZcySltLbCfUb0G9Qc,8614
|
|
5
|
-
ezgo/go2_vui.py,sha256=52I4Y8uqGkW9muDEnDzQ236HqVVTIgNIN_2gW5RTC48,10161
|
|
6
|
-
ezgo/ui.py,sha256=jUik6maaoemI2vsBM92OLZfJfskg54W7hwMls_gxppg,2777
|
|
7
|
-
ezgo-0.0.5.dist-info/LICENSE,sha256=Zk4eZBT3KaBhqM3LB_xN7QquwnoWsEHAoA6eLdE6W5M,1060
|
|
8
|
-
ezgo-0.0.5.dist-info/METADATA,sha256=Mooo_ZwJzl44ylJkPQHgsFUdV0nwCf0HZaxMuyKvsso,6869
|
|
9
|
-
ezgo-0.0.5.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
10
|
-
ezgo-0.0.5.dist-info/top_level.txt,sha256=BdCFEVD5V_4FxUtvH0BVlZKgxYnp7EKNsYs5OyFxj-g,5
|
|
11
|
-
ezgo-0.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|