ezgo 0.0.2__py3-none-any.whl → 0.0.4__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 +87 -0
- ezgo/camera.py +71 -0
- ezgo/go2.py +795 -0
- ezgo/go2_camera.py +290 -0
- ezgo/go2_vui.py +355 -0
- ezgo/ui.py +77 -0
- {ezgo-0.0.2.dist-info → ezgo-0.0.4.dist-info}/METADATA +12 -5
- ezgo-0.0.4.dist-info/RECORD +11 -0
- ezgo-0.0.4.dist-info/top_level.txt +1 -0
- ezgo-0.0.2.dist-info/RECORD +0 -5
- ezgo-0.0.2.dist-info/top_level.txt +0 -1
- {ezgo-0.0.2.dist-info → ezgo-0.0.4.dist-info}/LICENSE +0 -0
- {ezgo-0.0.2.dist-info → ezgo-0.0.4.dist-info}/WHEEL +0 -0
ezgo/go2_camera.py
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Go2机器狗摄像头控制类
|
|
3
|
+
提供图片获取和视频流功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import cv2
|
|
7
|
+
import numpy as np
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
|
|
11
|
+
from unitree_sdk2py.go2.video.video_client import VideoClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Go2Camera:
|
|
15
|
+
"""
|
|
16
|
+
Go2机器狗摄像头控制类
|
|
17
|
+
支持单张图片获取和视频流获取
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, interface=None, timeout=3.0):
|
|
21
|
+
"""
|
|
22
|
+
初始化摄像头控制器
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
interface (str): 网卡接口名称
|
|
26
|
+
timeout (float): 超时时间(秒)
|
|
27
|
+
"""
|
|
28
|
+
self.interface = interface
|
|
29
|
+
self.timeout = timeout
|
|
30
|
+
self.video_client = None
|
|
31
|
+
self.cap = None
|
|
32
|
+
self._streaming = False
|
|
33
|
+
self._stream_thread = None
|
|
34
|
+
self._latest_frame = None
|
|
35
|
+
self._frame_lock = threading.Lock()
|
|
36
|
+
|
|
37
|
+
def init(self, interface=None):
|
|
38
|
+
"""
|
|
39
|
+
初始化摄像头连接
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
interface (str): 网卡接口名称,如果为None则使用初始化时的接口
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
bool: 是否初始化成功
|
|
46
|
+
"""
|
|
47
|
+
if interface:
|
|
48
|
+
self.interface = interface
|
|
49
|
+
|
|
50
|
+
if not self.interface:
|
|
51
|
+
print("错误:未指定网络接口")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
# 初始化DDS通道(与go2.py保持一致)
|
|
56
|
+
ChannelFactoryInitialize(0, self.interface)
|
|
57
|
+
|
|
58
|
+
# 初始化视频客户端
|
|
59
|
+
self.video_client = VideoClient()
|
|
60
|
+
self.video_client.SetTimeout(self.timeout)
|
|
61
|
+
self.video_client.Init()
|
|
62
|
+
|
|
63
|
+
print(f"摄像头初始化成功,使用接口: {self.interface}")
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print(f"摄像头初始化失败: {e}")
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
def capture_image(self, save_path=None):
|
|
71
|
+
"""
|
|
72
|
+
获取一张图片
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
save_path (str): 保存路径,如果为None则不保存
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
numpy.ndarray: 图像数据,失败时返回None
|
|
79
|
+
"""
|
|
80
|
+
if not self.video_client:
|
|
81
|
+
print("错误:摄像头未初始化,请先调用 init()")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
# 获取图像样本
|
|
86
|
+
code, data = self.video_client.GetImageSample()
|
|
87
|
+
|
|
88
|
+
if code != 0 or data is None:
|
|
89
|
+
print(f"获取图像失败,错误码: {code}")
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
# 转换图像数据
|
|
93
|
+
image_data = np.frombuffer(bytes(data), dtype=np.uint8)
|
|
94
|
+
image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
|
|
95
|
+
|
|
96
|
+
if image is None:
|
|
97
|
+
print("图像解码失败")
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
print(f"成功获取图像,尺寸: {image.shape}")
|
|
101
|
+
|
|
102
|
+
# 保存图像
|
|
103
|
+
if save_path:
|
|
104
|
+
cv2.imwrite(save_path, image)
|
|
105
|
+
print(f"图像已保存到: {save_path}")
|
|
106
|
+
|
|
107
|
+
return image
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f"获取图像时出错: {e}")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
def open_video_stream(self, width=480, height=320):
|
|
114
|
+
"""
|
|
115
|
+
打开视频流
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
width (int): 视频宽度
|
|
119
|
+
height (int): 视频高度
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
bool: 是否成功打开视频流
|
|
123
|
+
"""
|
|
124
|
+
if not self.interface:
|
|
125
|
+
print("错误:未指定网络接口")
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
# 构建GStreamer字符串
|
|
129
|
+
gstreamer_str = (
|
|
130
|
+
f"udpsrc address=230.1.1.1 port=1720 multicast-iface={self.interface} "
|
|
131
|
+
"! application/x-rtp, media=video, encoding-name=H264 "
|
|
132
|
+
"! rtph264depay ! h264parse "
|
|
133
|
+
"! avdec_h264 " # 解码H.264
|
|
134
|
+
"! videoscale " # 添加缩放元素,用于调整分辨率
|
|
135
|
+
f"! video/x-raw,width={width},height={height} " # 目标分辨率
|
|
136
|
+
"! videoconvert ! video/x-raw, format=BGR " # 转换为OpenCV支持的BGR格式
|
|
137
|
+
"! appsink drop=1"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
self.cap = cv2.VideoCapture(gstreamer_str, cv2.CAP_GSTREAMER)
|
|
142
|
+
|
|
143
|
+
if not self.cap.isOpened():
|
|
144
|
+
print("视频流打开失败")
|
|
145
|
+
print(f"使用的网络接口: {self.interface}")
|
|
146
|
+
print("GStreamer字符串:", gstreamer_str)
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
print("视频流打开成功")
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f"打开视频流时出错: {e}")
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def read_frame(self):
|
|
157
|
+
"""
|
|
158
|
+
从视频流读取一帧
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
numpy.ndarray: 图像数据,失败时返回None
|
|
162
|
+
"""
|
|
163
|
+
if self.cap is None or not self.cap.isOpened():
|
|
164
|
+
print("错误:视频流未打开")
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
ret, frame = self.cap.read()
|
|
169
|
+
if not ret:
|
|
170
|
+
print("读取视频帧失败")
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
return frame
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
print(f"读取视频帧时出错: {e}")
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
def start_stream(self, width=480, height=320):
|
|
180
|
+
"""
|
|
181
|
+
开始后台视频流(持续获取最新帧)
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
width (int): 视频宽度
|
|
185
|
+
height (int): 视频高度
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
bool: 是否成功开始流
|
|
189
|
+
"""
|
|
190
|
+
if self._streaming:
|
|
191
|
+
print("视频流已在运行中")
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
if not self.open_video_stream(width, height):
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
self._streaming = True
|
|
198
|
+
|
|
199
|
+
def stream_loop():
|
|
200
|
+
while self._streaming:
|
|
201
|
+
frame = self.read_frame()
|
|
202
|
+
if frame is not None:
|
|
203
|
+
with self._frame_lock:
|
|
204
|
+
self._latest_frame = frame.copy()
|
|
205
|
+
time.sleep(0.01) # 避免过度占用CPU
|
|
206
|
+
|
|
207
|
+
self._stream_thread = threading.Thread(target=stream_loop)
|
|
208
|
+
self._stream_thread.daemon = True
|
|
209
|
+
self._stream_thread.start()
|
|
210
|
+
|
|
211
|
+
print("后台视频流已启动")
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
def get_latest_frame(self):
|
|
215
|
+
"""
|
|
216
|
+
获取最新的视频帧(非阻塞)
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
numpy.ndarray: 最新图像数据,无数据时返回None
|
|
220
|
+
"""
|
|
221
|
+
with self._frame_lock:
|
|
222
|
+
if self._latest_frame is not None:
|
|
223
|
+
return self._latest_frame.copy()
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
def stop_stream(self):
|
|
227
|
+
"""停止后台视频流"""
|
|
228
|
+
self._streaming = False
|
|
229
|
+
if self._stream_thread and self._stream_thread.is_alive():
|
|
230
|
+
self._stream_thread.join(timeout=1.0)
|
|
231
|
+
print("后台视频流已停止")
|
|
232
|
+
|
|
233
|
+
def close_video_stream(self):
|
|
234
|
+
"""关闭视频流"""
|
|
235
|
+
self.stop_stream()
|
|
236
|
+
if self.cap is not None:
|
|
237
|
+
self.cap.release()
|
|
238
|
+
self.cap = None
|
|
239
|
+
print("视频流已关闭")
|
|
240
|
+
|
|
241
|
+
def cleanup(self):
|
|
242
|
+
"""清理所有资源"""
|
|
243
|
+
self.stop_stream()
|
|
244
|
+
self.close_video_stream()
|
|
245
|
+
self.video_client = None
|
|
246
|
+
print("摄像头资源清理完成")
|
|
247
|
+
|
|
248
|
+
def __del__(self):
|
|
249
|
+
"""析构函数"""
|
|
250
|
+
try:
|
|
251
|
+
self.cleanup()
|
|
252
|
+
except:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# 便利函数
|
|
257
|
+
def capture_single_image(interface=None, save_path=None):
|
|
258
|
+
"""
|
|
259
|
+
便利函数:获取单张图片
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
interface (str): 网卡接口
|
|
263
|
+
save_path (str): 保存路径
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
numpy.ndarray: 图像数据
|
|
267
|
+
"""
|
|
268
|
+
camera = Go2Camera()
|
|
269
|
+
if camera.init(interface):
|
|
270
|
+
return camera.capture_image(save_path)
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def create_video_stream(interface=None, width=480, height=320):
|
|
275
|
+
"""
|
|
276
|
+
便利函数:创建视频流对象
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
interface (str): 网卡接口
|
|
280
|
+
width (int): 视频宽度
|
|
281
|
+
height (int): 视频高度
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Go2Camera: 摄像头对象
|
|
285
|
+
"""
|
|
286
|
+
camera = Go2Camera(interface)
|
|
287
|
+
if camera.init(interface):
|
|
288
|
+
if camera.open_video_stream(width, height):
|
|
289
|
+
return camera
|
|
290
|
+
return None
|
ezgo/go2_vui.py
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Go2机器狗声光控制函数库
|
|
4
|
+
提供LED灯光和音量控制功能
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import netifaces
|
|
11
|
+
import subprocess
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
|
|
15
|
+
from unitree_sdk2py.go2.vui.vui_client import VuiClient
|
|
16
|
+
VUI_AVAILABLE = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
print("警告: 无法导入VUI模块,声光控制功能不可用")
|
|
19
|
+
VUI_AVAILABLE = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Go2VUI:
|
|
23
|
+
"""Go2机器狗声光控制类"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, interface=None):
|
|
26
|
+
"""
|
|
27
|
+
初始化声光控制
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
interface: 网络接口名称,如'enx9c4782c277cd',None为自动检测
|
|
31
|
+
"""
|
|
32
|
+
self.interface = interface
|
|
33
|
+
self.client = None
|
|
34
|
+
self.initialized = False
|
|
35
|
+
|
|
36
|
+
if not VUI_AVAILABLE:
|
|
37
|
+
raise ImportError("VUI模块不可用,请检查unitree_sdk2py安装")
|
|
38
|
+
|
|
39
|
+
# 如果没有指定接口,自动获取
|
|
40
|
+
if self.interface is None:
|
|
41
|
+
self.get_interface()
|
|
42
|
+
|
|
43
|
+
def get_interface(self):
|
|
44
|
+
"""获取当前网络接口"""
|
|
45
|
+
try:
|
|
46
|
+
interfaces = netifaces.interfaces()
|
|
47
|
+
print(f"可用网络接口: {interfaces}")
|
|
48
|
+
|
|
49
|
+
# 优先查找以太网接口 (en开头)
|
|
50
|
+
for iface in interfaces:
|
|
51
|
+
if iface.startswith("en"):
|
|
52
|
+
self.interface = iface
|
|
53
|
+
print(f"自动选择网络接口: {self.interface}")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
# 如果没有en开头的,查找其他可能的接口
|
|
57
|
+
for iface in interfaces:
|
|
58
|
+
if iface.startswith("eth") or iface.startswith("wlan"):
|
|
59
|
+
self.interface = iface
|
|
60
|
+
print(f"自动选择网络接口: {self.interface}")
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
# 如果都没找到,使用第一个接口
|
|
64
|
+
if interfaces:
|
|
65
|
+
self.interface = interfaces[0]
|
|
66
|
+
print(f"使用第一个可用接口: {self.interface}")
|
|
67
|
+
else:
|
|
68
|
+
print("警告: 未找到可用的网络接口")
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
print(f"获取网络接口失败: {e}")
|
|
72
|
+
|
|
73
|
+
def check_go2_connection(self):
|
|
74
|
+
"""检查机器狗IP连通性"""
|
|
75
|
+
try:
|
|
76
|
+
# 使用ping检查机器狗IP (192.168.123.161是Go2的默认IP)
|
|
77
|
+
if sys.platform.startswith('win'):
|
|
78
|
+
# Windows系统
|
|
79
|
+
result = subprocess.run(['ping', '-n', '1', '-w', '2000', '192.168.123.161'],
|
|
80
|
+
capture_output=True, text=True, timeout=5)
|
|
81
|
+
else:
|
|
82
|
+
# Linux/Mac系统
|
|
83
|
+
result = subprocess.run(['ping', '-c', '1', '-W', '2', '192.168.123.161'],
|
|
84
|
+
capture_output=True, text=True, timeout=5)
|
|
85
|
+
return result.returncode == 0
|
|
86
|
+
except subprocess.TimeoutExpired:
|
|
87
|
+
print("ping超时")
|
|
88
|
+
return False
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"检查连接时出错: {e}")
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
def init(self):
|
|
94
|
+
"""初始化VUI客户端"""
|
|
95
|
+
try:
|
|
96
|
+
# 检查机器狗IP连通性
|
|
97
|
+
if not self.check_go2_connection():
|
|
98
|
+
print("无法连接到机器狗,请检查网络连接")
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
# 初始化DDS通道
|
|
102
|
+
if self.interface:
|
|
103
|
+
ChannelFactoryInitialize(0, self.interface)
|
|
104
|
+
else:
|
|
105
|
+
ChannelFactoryInitialize(0)
|
|
106
|
+
|
|
107
|
+
# 初始化VUI客户端
|
|
108
|
+
self.client = VuiClient()
|
|
109
|
+
self.client.SetTimeout(3.0)
|
|
110
|
+
self.client.Init()
|
|
111
|
+
|
|
112
|
+
self.initialized = True
|
|
113
|
+
print("VUI声光控制初始化成功")
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
print(f"VUI初始化失败: {e}")
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
def set_brightness(self, level):
|
|
121
|
+
"""
|
|
122
|
+
设置LED亮度
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
level (int): 亮度级别 0-10,0为关闭
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
bool: 设置是否成功
|
|
129
|
+
"""
|
|
130
|
+
if not self.initialized:
|
|
131
|
+
print("VUI未初始化")
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
if level < 0 or level > 10:
|
|
136
|
+
print("亮度级别必须在0-10之间")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
code = self.client.SetBrightness(level)
|
|
140
|
+
|
|
141
|
+
if code == 0:
|
|
142
|
+
print(f"亮度设置成功: {level}")
|
|
143
|
+
return True
|
|
144
|
+
else:
|
|
145
|
+
print(f"亮度设置失败,错误码: {code}")
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
print(f"设置亮度异常: {e}")
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
def get_brightness(self):
|
|
153
|
+
"""
|
|
154
|
+
获取当前LED亮度
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
tuple: (success, level) success为bool,level为int
|
|
158
|
+
"""
|
|
159
|
+
if not self.initialized:
|
|
160
|
+
print("VUI未初始化")
|
|
161
|
+
return False, 0
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
code, level = self.client.GetBrightness()
|
|
165
|
+
|
|
166
|
+
if code == 0:
|
|
167
|
+
print(f"当前亮度: {level}")
|
|
168
|
+
return True, level
|
|
169
|
+
else:
|
|
170
|
+
print(f"获取亮度失败,错误码: {code}")
|
|
171
|
+
return False, 0
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"获取亮度异常: {e}")
|
|
175
|
+
return False, 0
|
|
176
|
+
|
|
177
|
+
def set_volume(self, level):
|
|
178
|
+
"""
|
|
179
|
+
设置音量
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
level (int): 音量级别 0-10,0为静音
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
bool: 设置是否成功
|
|
186
|
+
"""
|
|
187
|
+
if not self.initialized:
|
|
188
|
+
print("VUI未初始化")
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
if level < 0 or level > 10:
|
|
193
|
+
print("音量级别必须在0-10之间")
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
code = self.client.SetVolume(level)
|
|
197
|
+
|
|
198
|
+
if code == 0:
|
|
199
|
+
print(f"音量设置成功: {level}")
|
|
200
|
+
return True
|
|
201
|
+
else:
|
|
202
|
+
print(f"音量设置失败,错误码: {code}")
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
except Exception as e:
|
|
206
|
+
print(f"设置音量异常: {e}")
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
def get_volume(self):
|
|
210
|
+
"""
|
|
211
|
+
获取当前音量
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
tuple: (success, level) success为bool,level为int
|
|
215
|
+
"""
|
|
216
|
+
if not self.initialized:
|
|
217
|
+
print("VUI未初始化")
|
|
218
|
+
return False, 0
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
code, level = self.client.GetVolume()
|
|
222
|
+
|
|
223
|
+
if code == 0:
|
|
224
|
+
print(f"当前音量: {level}")
|
|
225
|
+
return True, level
|
|
226
|
+
else:
|
|
227
|
+
print(f"获取音量失败,错误码: {code}")
|
|
228
|
+
return False, 0
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
print(f"获取音量异常: {e}")
|
|
232
|
+
return False, 0
|
|
233
|
+
|
|
234
|
+
def lights_off(self):
|
|
235
|
+
"""关闭LED灯光"""
|
|
236
|
+
return self.set_brightness(0)
|
|
237
|
+
|
|
238
|
+
def mute(self):
|
|
239
|
+
"""静音"""
|
|
240
|
+
return self.set_volume(0)
|
|
241
|
+
|
|
242
|
+
def max_brightness(self):
|
|
243
|
+
"""最大亮度"""
|
|
244
|
+
return self.set_brightness(10)
|
|
245
|
+
|
|
246
|
+
def max_volume(self):
|
|
247
|
+
"""最大音量"""
|
|
248
|
+
return self.set_volume(10)
|
|
249
|
+
|
|
250
|
+
def breathing_light(self, cycles=3, delay=0.5):
|
|
251
|
+
"""
|
|
252
|
+
呼吸灯效果
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
cycles (int): 循环次数
|
|
256
|
+
delay (float): 每级延迟时间(秒)
|
|
257
|
+
"""
|
|
258
|
+
print(f"开始呼吸灯效果,循环{cycles}次")
|
|
259
|
+
|
|
260
|
+
for cycle in range(cycles):
|
|
261
|
+
# 渐亮
|
|
262
|
+
for level in range(1, 11):
|
|
263
|
+
self.set_brightness(level)
|
|
264
|
+
time.sleep(delay)
|
|
265
|
+
|
|
266
|
+
# 渐暗
|
|
267
|
+
for level in range(9, 0, -1):
|
|
268
|
+
self.set_brightness(level)
|
|
269
|
+
time.sleep(delay)
|
|
270
|
+
|
|
271
|
+
# 关闭灯光
|
|
272
|
+
self.lights_off()
|
|
273
|
+
print("呼吸灯效果结束")
|
|
274
|
+
|
|
275
|
+
def volume_test(self, max_level=8):
|
|
276
|
+
"""
|
|
277
|
+
音量测试
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
max_level (int): 测试最大音量级别
|
|
281
|
+
"""
|
|
282
|
+
print(f"开始音量测试,最大级别: {max_level}")
|
|
283
|
+
|
|
284
|
+
for level in range(1, max_level + 1):
|
|
285
|
+
self.set_volume(level)
|
|
286
|
+
time.sleep(1)
|
|
287
|
+
|
|
288
|
+
# 恢复适中音量
|
|
289
|
+
self.set_volume(5)
|
|
290
|
+
print("音量测试结束")
|
|
291
|
+
|
|
292
|
+
def cleanup(self):
|
|
293
|
+
"""清理资源"""
|
|
294
|
+
try:
|
|
295
|
+
if self.client:
|
|
296
|
+
# 关闭灯光和音量
|
|
297
|
+
self.lights_off()
|
|
298
|
+
self.mute()
|
|
299
|
+
print("VUI资源清理完成")
|
|
300
|
+
except Exception as e:
|
|
301
|
+
print(f"VUI清理异常: {e}")
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# 便捷函数
|
|
305
|
+
def create_vui(interface=None):
|
|
306
|
+
"""
|
|
307
|
+
创建VUI实例的便捷函数
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
interface: 网络接口名称,如'enx9c4782c277cd',None为自动检测
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Go2VUI: VUI实例,初始化失败返回None
|
|
314
|
+
"""
|
|
315
|
+
vui = Go2VUI(interface)
|
|
316
|
+
if vui.init():
|
|
317
|
+
return vui
|
|
318
|
+
else:
|
|
319
|
+
return None
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def auto_create_vui():
|
|
323
|
+
"""
|
|
324
|
+
自动创建VUI实例,自动检测网络接口
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Go2VUI: VUI实例,初始化失败返回None
|
|
328
|
+
"""
|
|
329
|
+
return create_vui(None)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
if __name__ == "__main__":
|
|
333
|
+
# 测试代码
|
|
334
|
+
vui = Go2VUI()
|
|
335
|
+
|
|
336
|
+
if vui.init():
|
|
337
|
+
print("=== VUI功能测试 ===")
|
|
338
|
+
|
|
339
|
+
# 测试亮度控制
|
|
340
|
+
vui.set_brightness(5)
|
|
341
|
+
time.sleep(1)
|
|
342
|
+
vui.get_brightness()
|
|
343
|
+
|
|
344
|
+
# 测试音量控制
|
|
345
|
+
vui.set_volume(3)
|
|
346
|
+
time.sleep(1)
|
|
347
|
+
vui.get_volume()
|
|
348
|
+
|
|
349
|
+
# 呼吸灯效果
|
|
350
|
+
vui.breathing_light(2, 0.3)
|
|
351
|
+
|
|
352
|
+
# 清理
|
|
353
|
+
vui.cleanup()
|
|
354
|
+
else:
|
|
355
|
+
print("VUI初始化失败")
|