ezgo 0.0.3__py3-none-any.whl → 0.0.5__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/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初始化失败")