Browse Source

feat: add qr vision service

ucar 1 month ago
parent
commit
24deabd9ff

+ 22 - 0
qr_vision/CMakeLists.txt

@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 2.8.3)
+project(qr_vision)
+
+find_package(catkin REQUIRED COMPONENTS
+  rospy
+  std_msgs
+  sensor_msgs
+  cv_bridge
+  message_generation
+)
+
+add_service_files(FILES ScanQRCodes.srv)
+
+generate_messages(DEPENDENCIES std_msgs)
+
+catkin_package(
+  CATKIN_DEPENDS rospy std_msgs sensor_msgs cv_bridge message_runtime
+)
+
+catkin_install_python(PROGRAMS scripts/qr_scanner_node.py
+  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+)

+ 22 - 0
qr_vision/package.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<package format="2">
+  <name>qr_vision</name>
+  <version>0.0.0</version>
+  <description>QR code vision and LLM decision</description>
+  <maintainer email="user@todo.com">user</maintainer>
+  <license>BSD</license>
+
+  <buildtool_depend>catkin</buildtool_depend>
+
+  <build_depend>rospy</build_depend>
+  <build_depend>std_msgs</build_depend>
+  <build_depend>sensor_msgs</build_depend>
+  <build_depend>cv_bridge</build_depend>
+  <build_depend>message_generation</build_depend>
+
+  <exec_depend>rospy</exec_depend>
+  <exec_depend>std_msgs</exec_depend>
+  <exec_depend>sensor_msgs</exec_depend>
+  <exec_depend>cv_bridge</exec_depend>
+  <exec_depend>message_runtime</exec_depend>
+</package>

+ 99 - 0
qr_vision/scripts/qr_scanner_node.py

@@ -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

+ 6 - 0
qr_vision/srv/ScanQRCodes.srv

@@ -0,0 +1,6 @@
+string real_category
+string simulation_category
+---
+string[] product_names
+string real_product
+string simulation_product