|
@@ -0,0 +1,99 @@
|
|
|
|
|
+#!/usr/bin/env python3
|
|
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
|
|
+import rospy
|
|
|
|
|
+import cv2
|
|
|
|
|
+import json
|
|
|
|
|
+import requests
|
|
|
|
|
+import sys
|
|
|
|
|
+from pyzbar.pyzbar import decode
|
|
|
|
|
+from sensor_msgs.msg import Image
|
|
|
|
|
+from cv_bridge import CvBridge
|
|
|
|
|
+from qr_vision.srv import ScanQRCodes, ScanQRCodesResponse
|
|
|
|
|
+
|
|
|
|
|
+sys.path.append('/home/ucar/ucar_ws/src/SparkTalk')
|
|
|
|
|
+from SparkMain import build_prompt, ask_xinghuo, clean_json_response, validate_result_structure
|
|
|
|
|
+
|
|
|
|
|
+class QRVisionNode:
|
|
|
|
|
+ def __init__(self):
|
|
|
|
|
+ self.bridge = CvBridge()
|
|
|
|
|
+ self.product_names = []
|
|
|
|
|
+ self.scanned_urls = set()
|
|
|
|
|
+
|
|
|
|
|
+ rospy.init_node('qr_vision_node', anonymous=True)
|
|
|
|
|
+ rospy.Subscriber('/ucar_camera/image_raw', Image, self.image_callback)
|
|
|
|
|
+ rospy.Service('/scan_qrcodes', ScanQRCodes, self.handle_scan_request)
|
|
|
|
|
+ rospy.loginfo("二维码视觉节点已启动")
|
|
|
|
|
+
|
|
|
|
|
+ def image_callback(self, img_msg):
|
|
|
|
|
+ if len(self.product_names) >= 3:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ frame = self.bridge.imgmsg_to_cv2(img_msg, "rgb8")
|
|
|
|
|
+ frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
|
|
|
|
+
|
|
|
|
|
+ for obj in decode(frame_bgr):
|
|
|
|
|
+ url = obj.data.decode('utf-8')
|
|
|
|
|
+ if url in self.scanned_urls:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ rospy.loginfo("识别到二维码URL: %s", url)
|
|
|
|
|
+ try:
|
|
|
|
|
+ resp = requests.get(url, timeout=5)
|
|
|
|
|
+ data = resp.json()
|
|
|
|
|
+ if data.get("code") == 200:
|
|
|
|
|
+ product = data["result"]
|
|
|
|
|
+ self.product_names.append(product)
|
|
|
|
|
+ self.scanned_urls.add(url)
|
|
|
|
|
+ rospy.loginfo("货品: %s (%d/3)", product, len(self.product_names))
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ rospy.logwarn("请求URL失败: %s", str(e))
|
|
|
|
|
+
|
|
|
|
|
+ def handle_scan_request(self, req):
|
|
|
|
|
+ real_cat = req.real_category
|
|
|
|
|
+ sim_cat = req.simulation_category
|
|
|
|
|
+
|
|
|
|
|
+ timeout = rospy.Time.now() + rospy.Duration(15)
|
|
|
|
|
+ while len(self.product_names) < 3 and rospy.Time.now() < timeout:
|
|
|
|
|
+ rospy.sleep(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ if len(self.product_names) < 3:
|
|
|
|
|
+ rospy.logwarn("只识别到%d个二维码", len(self.product_names))
|
|
|
|
|
+
|
|
|
|
|
+ rospy.loginfo("识别到的货品: %s", str(self.product_names))
|
|
|
|
|
+
|
|
|
|
|
+ prompt = build_prompt(real_cat, sim_cat, self.product_names)
|
|
|
|
|
+ raw_response = ask_xinghuo(prompt)
|
|
|
|
|
+
|
|
|
|
|
+ real_product = ""
|
|
|
|
|
+ sim_product = ""
|
|
|
|
|
+ try:
|
|
|
|
|
+ cleaned = clean_json_response(raw_response)
|
|
|
|
|
+ result = json.loads(cleaned)
|
|
|
|
|
+ is_valid, errors = validate_result_structure(
|
|
|
|
|
+ result, real_cat, sim_cat, self.product_names)
|
|
|
|
|
+
|
|
|
|
|
+ if is_valid:
|
|
|
|
|
+ real_product = result["real_task"]["matched_product"]
|
|
|
|
|
+ sim_product = result["simulation_task"]["matched_product"]
|
|
|
|
|
+ rospy.loginfo("决策成功: 真实=%s 仿真=%s", real_product, sim_product)
|
|
|
|
|
+ else:
|
|
|
|
|
+ rospy.logwarn("校验失败: %s", str(errors))
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ rospy.logerr("JSON解析失败: %s", str(e))
|
|
|
|
|
+
|
|
|
|
|
+ saved_products = list(self.product_names)
|
|
|
|
|
+ self.product_names = []
|
|
|
|
|
+ self.scanned_urls = set()
|
|
|
|
|
+
|
|
|
|
|
+ return ScanQRCodesResponse(
|
|
|
|
|
+ product_names=saved_products,
|
|
|
|
|
+ real_product=real_product,
|
|
|
|
|
+ simulation_product=sim_product
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
|
+ try:
|
|
|
|
|
+ QRVisionNode()
|
|
|
|
|
+ rospy.spin()
|
|
|
|
|
+ except rospy.ROSInterruptException:
|
|
|
|
|
+ pass
|