first commit
This commit is contained in:
		
							parent
							
								
									9ec3f5efea
								
							
						
					
					
						commit
						6ec04089f4
					
				
							
								
								
									
										269
									
								
								track_object.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								track_object.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,269 @@ | |||||||
|  | import time | ||||||
|  | import threading | ||||||
|  | import re | ||||||
|  | import numpy as np | ||||||
|  | import sys | ||||||
|  | import os | ||||||
|  | import signal | ||||||
|  | from openai import OpenAI | ||||||
|  | from picamera2 import Picamera2 | ||||||
|  | import io | ||||||
|  | from PIL import Image | ||||||
|  | import base64 | ||||||
|  | 
 | ||||||
|  | # 配置项目路径(根据你的实际路径修改) | ||||||
|  | PROJECT_ROOT = "/home/duckpi/open_duck_mini_ws/OPEN_DUCK_MINI/Open_Duck_Mini_Runtime-2" | ||||||
|  | ONNX_MODEL_PATH = "/home/duckpi/open_duck_mini_ws/OPEN_DUCK_MINI/Open_Duck_Mini-2/BEST_WALK_ONNX_2.onnx" | ||||||
|  | sys.path.append(PROJECT_ROOT) | ||||||
|  | 
 | ||||||
|  | # 导入运动控制模块 | ||||||
|  | from v2_rl_walk_mujoco import RLWalk | ||||||
|  | 
 | ||||||
|  | # API配置(替换为你的实际密钥) | ||||||
|  | ARK_API_KEY = "390d517c-129a-41c1-bf3d-458048007b69" | ||||||
|  | ARK_MODEL_ID = "doubao-seed-1-6-250615" | ||||||
|  | 
 | ||||||
|  | class SimpleTTS: | ||||||
|  |     """简化的TTS模块,仅用于测试反馈""" | ||||||
|  |     def speak(self, text): | ||||||
|  |         print(f"[语音反馈] {text}") | ||||||
|  | 
 | ||||||
|  | class MotionController: | ||||||
|  |     """运动控制封装""" | ||||||
|  |     def __init__(self): | ||||||
|  |         try: | ||||||
|  |             self.rl_walk = RLWalk( | ||||||
|  |                 onnx_model_path=ONNX_MODEL_PATH, | ||||||
|  |                 cutoff_frequency=40, | ||||||
|  |                 pid=[30, 0, 0] | ||||||
|  |             ) | ||||||
|  |             self.walk_thread = threading.Thread(target=self.rl_walk.run, daemon=True) | ||||||
|  |             self.walk_thread.start() | ||||||
|  |             time.sleep(1) | ||||||
|  |             print("✅ 运动控制模块初始化成功") | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"❌ 运动控制初始化失败:{str(e)}") | ||||||
|  |             sys.exit(1) | ||||||
|  |      | ||||||
|  |     def execute_motion(self, action_name: str, seconds: float): | ||||||
|  |         """执行指定动作""" | ||||||
|  |         try: | ||||||
|  |             if action_name == "move_forward": | ||||||
|  |                 print(f"🚶 前进{seconds}秒...") | ||||||
|  |                 self.rl_walk.last_commands[0] = 0.17 | ||||||
|  |             elif action_name == "move_backward": | ||||||
|  |                 print(f"🚶 后退{seconds}秒...") | ||||||
|  |                 self.rl_walk.last_commands[0] = -0.17 | ||||||
|  |             elif action_name == "turn_left": | ||||||
|  |                 print(f"🔄 左转{seconds}秒...") | ||||||
|  |                 self.rl_walk.last_commands[2] = 1.1 | ||||||
|  |             elif action_name == "turn_right": | ||||||
|  |                 print(f"🔄 右转{seconds}秒...") | ||||||
|  |                 self.rl_walk.last_commands[2] = -1.1 | ||||||
|  |              | ||||||
|  |             time.sleep(seconds) | ||||||
|  |             self.rl_walk.last_commands = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | ||||||
|  |             print("✅ 动作完成") | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"❌ 动作执行失败:{str(e)}") | ||||||
|  |             self.rl_walk.last_commands = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | ||||||
|  | 
 | ||||||
|  | class CameraModule: | ||||||
|  |     """摄像头模块封装""" | ||||||
|  |     def __init__(self): | ||||||
|  |         try: | ||||||
|  |             self.camera = Picamera2() | ||||||
|  |             cam_config = self.camera.create_still_configuration(main={"size": (320, 240)}) | ||||||
|  |             self.camera.configure(cam_config) | ||||||
|  |             self.camera.start() | ||||||
|  |             print("✅ 摄像头模块初始化成功") | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"❌ 摄像头初始化失败:{str(e)}") | ||||||
|  |             sys.exit(1) | ||||||
|  |      | ||||||
|  |     def capture_base64(self): | ||||||
|  |         """拍摄并返回base64编码图像""" | ||||||
|  |         try: | ||||||
|  |             img_array = self.camera.capture_array() | ||||||
|  |             img_byte = io.BytesIO() | ||||||
|  |             Image.fromarray(img_array).save(img_byte, format="JPEG", quality=80) | ||||||
|  |             return base64.b64encode(img_byte.getvalue()).decode("utf-8") | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"❌ 拍摄失败:{str(e)}") | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  | class ObjectTracker: | ||||||
|  |     """物品追踪核心类""" | ||||||
|  |     def __init__(self, target_name="万用表", tts=None, motion=None, camera=None): | ||||||
|  |         self.target_name = target_name | ||||||
|  |         self.tracking_active = False | ||||||
|  |         self.target_lost_count = 0 | ||||||
|  |         self.max_lost_count = 5  # 连续丢失次数阈值 | ||||||
|  |         self.tts = tts or SimpleTTS() | ||||||
|  |         self.motion = motion or MotionController() | ||||||
|  |         self.camera = camera or CameraModule() | ||||||
|  |         self.client = OpenAI( | ||||||
|  |             base_url="https://ark.cn-beijing.volces.com/api/v3", | ||||||
|  |             api_key=ARK_API_KEY | ||||||
|  |         ) | ||||||
|  |         print(f"✅ 追踪器初始化完成,目标:{self.target_name}") | ||||||
|  | 
 | ||||||
|  |     def get_object_position(self, image_base64): | ||||||
|  |         """获取目标在图像中的位置信息""" | ||||||
|  |         try: | ||||||
|  |             response = self.client.chat.completions.create( | ||||||
|  |                 model=ARK_MODEL_ID, | ||||||
|  |                 messages=[{ | ||||||
|  |                     "role": "user", | ||||||
|  |                     "content": [ | ||||||
|  |                         {"type": "image_url", "image_url": f"data:image/jpeg;base64,{image_base64}"}, | ||||||
|  |                         {"type": "text", "text": (f"请识别图像中的'{self.target_name}',返回其位置信息。" | ||||||
|  |                                                "格式要求:中心X坐标(0-100),中心Y坐标(0-100),宽度占比(0-100),高度占比(0-100)。" | ||||||
|  |                                                "如果未找到,返回'未找到'")} | ||||||
|  |                     ] | ||||||
|  |                 }] | ||||||
|  |             ) | ||||||
|  |              | ||||||
|  |             content = response.choices[0].message.content | ||||||
|  |             if "未找到" in content: | ||||||
|  |                 return None | ||||||
|  |                  | ||||||
|  |             # 提取数字信息 | ||||||
|  |             nums = re.findall(r"\d+\.?\d*", content) | ||||||
|  |             if len(nums) >= 4: | ||||||
|  |                 return { | ||||||
|  |                     "center_x": float(nums[0]), | ||||||
|  |                     "center_y": float(nums[1]), | ||||||
|  |                     "width": float(nums[2]), | ||||||
|  |                     "height": float(nums[3]) | ||||||
|  |                 } | ||||||
|  |             return None | ||||||
|  |              | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"❌ 目标识别出错: {str(e)}") | ||||||
|  |             return None | ||||||
|  |      | ||||||
|  |     def track_object(self): | ||||||
|  |         """追踪主循环""" | ||||||
|  |         self.tts.speak(f"开始追踪{self.target_name}") | ||||||
|  |         self.tracking_active = True | ||||||
|  |         self.target_lost_count = 0 | ||||||
|  |          | ||||||
|  |         try: | ||||||
|  |             while self.tracking_active: | ||||||
|  |                 # 1. 采集图像 | ||||||
|  |                 image_base64 = self.camera.capture_base64() | ||||||
|  |                 if not image_base64: | ||||||
|  |                     time.sleep(1) | ||||||
|  |                     continue | ||||||
|  |                  | ||||||
|  |                 # 2. 识别目标位置 | ||||||
|  |                 pos = self.get_object_position(image_base64) | ||||||
|  |                 if not pos: | ||||||
|  |                     self.target_lost_count += 1 | ||||||
|  |                     print(f"⚠️ 未找到{self.target_name},已连续丢失{self.target_lost_count}次") | ||||||
|  |                      | ||||||
|  |                     if self.target_lost_count >= self.max_lost_count: | ||||||
|  |                         self.tts.speak(f"已丢失{self.target_name},停止追踪") | ||||||
|  |                         self.stop_tracking() | ||||||
|  |                     time.sleep(1) | ||||||
|  |                     continue | ||||||
|  |                  | ||||||
|  |                 # 重置丢失计数 | ||||||
|  |                 self.target_lost_count = 0 | ||||||
|  |                 print(f"🎯 发现{self.target_name} - 中心X: {pos['center_x']}, 宽度占比: {pos['width']}") | ||||||
|  |                  | ||||||
|  |                 # 3. 根据位置控制移动 | ||||||
|  |                 self.control_movement(pos) | ||||||
|  |                 time.sleep(1.5)  # 控制追踪频率 | ||||||
|  |                  | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"❌ 追踪过程出错: {str(e)}") | ||||||
|  |             self.stop_tracking() | ||||||
|  |      | ||||||
|  |     def control_movement(self, pos): | ||||||
|  |         """根据目标位置控制移动""" | ||||||
|  |         # 横向位置控制 (左右转向) | ||||||
|  |         if pos["center_x"] < 35:  # 目标偏左 | ||||||
|  |             self.tts.speak("目标在左边,向左转") | ||||||
|  |             self.motion.execute_motion("turn_left", 0.8) | ||||||
|  |         elif pos["center_x"] > 65:  # 目标偏右 | ||||||
|  |             self.tts.speak("目标在右边,向右转") | ||||||
|  |             self.motion.execute_motion("turn_right", 0.8) | ||||||
|  |          | ||||||
|  |         # 距离控制 (前进后退) | ||||||
|  |         if pos["width"] < 20:  # 目标过小,距离过远 | ||||||
|  |             self.tts.speak("距离目标较远,前进") | ||||||
|  |             self.motion.execute_motion("move_forward", 1.5) | ||||||
|  |         elif pos["width"] > 40:  # 目标过大,距离过近 | ||||||
|  |             self.tts.speak("距离目标过近,后退") | ||||||
|  |             self.motion.execute_motion("move_backward", 1) | ||||||
|  |         else: | ||||||
|  |             self.tts.speak("已对准目标,保持位置") | ||||||
|  |      | ||||||
|  |     def start_tracking(self): | ||||||
|  |         """启动追踪线程""" | ||||||
|  |         if not self.tracking_active: | ||||||
|  |             threading.Thread(target=self.track_object, daemon=True).start() | ||||||
|  |      | ||||||
|  |     def stop_tracking(self): | ||||||
|  |         """停止追踪""" | ||||||
|  |         self.tracking_active = False | ||||||
|  |         print(f"🛑 停止追踪{self.target_name}") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     # 初始化组件 | ||||||
|  |     tts = SimpleTTS() | ||||||
|  |     motion = MotionController() | ||||||
|  |     camera = CameraModule() | ||||||
|  |     tracker = ObjectTracker( | ||||||
|  |         target_name="万用表",  # 可修改为其他目标,如"水杯"、"书本" | ||||||
|  |         tts=tts, | ||||||
|  |         motion=motion, | ||||||
|  |         camera=camera | ||||||
|  |     ) | ||||||
|  |      | ||||||
|  |     # 信号处理(优雅退出) | ||||||
|  |     def handle_interrupt(signum, frame): | ||||||
|  |         print("\n🛑 收到退出信号,正在停止...") | ||||||
|  |         tracker.stop_tracking() | ||||||
|  |         motion.rl_walk.last_commands = [0.0, 0.0, 0.0]  # 停止运动 | ||||||
|  |         camera.camera.stop()  # 关闭摄像头 | ||||||
|  |         print("✅ 所有资源已释放,程序退出") | ||||||
|  |         sys.exit(0) | ||||||
|  |      | ||||||
|  |     signal.signal(signal.SIGINT, handle_interrupt) | ||||||
|  |      | ||||||
|  |     # 交互提示 | ||||||
|  |     print("\n===== 物品追踪测试程序 =====") | ||||||
|  |     print("操作说明:") | ||||||
|  |     print("  s - 开始追踪目标") | ||||||
|  |     print("  t - 停止追踪") | ||||||
|  |     print("  q - 退出程序") | ||||||
|  |     print("===========================") | ||||||
|  |      | ||||||
|  |     # 主交互循环 | ||||||
|  |     while True: | ||||||
|  |         cmd = input("请输入指令: ").strip().lower() | ||||||
|  |         if cmd == 's': | ||||||
|  |             if not tracker.tracking_active: | ||||||
|  |                 print("▶️ 开始追踪...") | ||||||
|  |                 tracker.start_tracking() | ||||||
|  |             else: | ||||||
|  |                 print("⚠️ 已经在追踪中") | ||||||
|  |         elif cmd == 't': | ||||||
|  |             if tracker.tracking_active: | ||||||
|  |                 tracker.stop_tracking() | ||||||
|  |                 print("⏹️ 已停止追踪") | ||||||
|  |             else: | ||||||
|  |                 print("⚠️ 没有正在进行的追踪") | ||||||
|  |         elif cmd == 'q': | ||||||
|  |             handle_interrupt(None, None) | ||||||
|  |         else: | ||||||
|  |             print("❓ 未知指令,请输入 s/t/q") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |      | ||||||
|  |     main() | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user