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