aruco位姿检测

aruco 生成

aruco在线生成网站

aruco位姿检测

cpp 复制代码
import cv2
import sys
import copy
import zmq
import numpy as np
import math
import keyboard
import threading
import time
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import JointState

class Aruco_Process:
    def __init__(self, camera_dev = 0, marker_size = 0.051) -> None:
        
        # 相机设置
        self.cap = cv2.VideoCapture(camera_dev)
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)   # 设置视频帧宽度
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960)  # 设置视频帧高度
        self.position_pre = np.zeros(3)    # 上一帧的位置, 用于平滑
        self.euler_angles_pre = np.zeros(3)    # 上一帧的姿态欧拉角, 用于平滑
        self.k = 0.4
        self.position_rate = 0.8
        if not self.cap.isOpened():
            print("无法打开摄像头")
            exit()
    
    
        self.marker_size = marker_size
        # 创建检测参数
        self.parameters = cv2.aruco.DetectorParameters_create()
        # 调整检测参数以提高检测率
        self.parameters.adaptiveThreshWinSizeMin = 3
        self.parameters.adaptiveThreshWinSizeMax = 23
        self.parameters.adaptiveThreshWinSizeStep = 10
        self.parameters.adaptiveThreshConstant = 7
        self.parameters.minMarkerPerimeterRate = 0.03
        self.parameters.maxMarkerPerimeterRate = 4.0
        self.parameters.polygonalApproxAccuracyRate = 0.05
        self.parameters.minCornerDistanceRate = 0.05
        self.parameters.minMarkerDistanceRate = 0.05
        self.parameters.minDistanceToBorder = 3
            
        frame = self.update_frame()
        # 获取图像尺寸
        height, width = frame.shape[:2]
        # 估计相机内参(因为没有实际标定数据)
        # 假设相机的焦距约为图像宽度,主点在图像中心
        focal_length = width
        center = (width / 2, height / 2)
        self.camera_matrix = np.array(
            [[focal_length, 0, center[0]],
            [0, focal_length, center[1]],
            [0, 0, 1]], dtype=np.float32
        )
        # 假设没有镜头畸变
        self.dist_coeffs = np.zeros((4, 1), dtype=np.float32)
        
        
    def update_frame(self):
        # 读取一帧
        ret, frame = self.cap.read()

        if not ret:
            print("无法获取视频帧")
            exit()
        else:
            return frame
        
    
    def detect_aruco_markers_with_pose(self, image, is_show = True):
        """
        检测图像中的ArUco标记并计算摄像头位姿
        参数:
            image: 图像
            camera_matrix: 相机内参矩阵
            dist_coeffs: 畸变矩阵
        返回:
            标记后的图像和位姿信息
        """

        # 实际应用中记得替换为您的实际相机内参。YOUR_FX,YOUR_FY为焦距.YOUR_CX,YOUR_CY为主点.
        # camera_matrix = np.array([
        #     [YOUR_FX, 0, YOUR_CX],
        #     [0, YOUR_FY, YOUR_CY],
        #     [0, 0, 1]
        # ], dtype=np.float32)
    
        # 实际应用替换为实际的5个畸变系数
        # dist_coeffs = np.array([[k1], [k2], [p1], [p2], [k3]], dtype=np.float32)
        # 或者如果只有4个:
        # dist_coeffs = np.array([[k1], [k2], [p1], [p2]], dtype=np.float32)
    
        # 转换为灰度图像
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
        # 定义要尝试的ArUco字典列表
        dict_type = cv2.aruco.DICT_6X6_50
    
        # 字典名称映射(用于输出)
        dict_names = {
            cv2.aruco.DICT_4X4_50: "DICT_4X4_50",
            cv2.aruco.DICT_6X6_50: "DICT_6X6_50",
        }
    
        # 创建结果图像的副本
        result_image = image.copy()
        
        try:
            # 适配新版本OpenCV API
            aruco_dict = cv2.aruco.getPredefinedDictionary(dict_type)

            # 检测ArUco标记           
            corners, ids, rejected = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=self.parameters)

            # 如果检测到标记
            if ids is not None and len(ids) > 0:
                if len(ids) > 1:
                    print("Detect more than one aruco, Wrong !!!")
                    return False, result_image, None
                
                if is_show:
                    # 绘制检测到的标记
                    cv2.aruco.drawDetectedMarkers(result_image, corners, ids)
                
                    
                    # 计算标记中心点
                    c = corners[0][0]
                    center_point = np.mean(c, axis=0).astype(int)

                    # 添加ID文本
                    marker_id = ids[0][0]
                    cv2.putText(
                        result_image,
                        f"ID: {marker_id}",
                        (center_point[0]-20, center_point[1]),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.6,
                        (0, 255, 0),
                        1
                    )

                # 估计姿态
                # 重新整理角点格式以适应estimatePoseSingleMarkers
                marker_corners = np.array([corners[0]], dtype=np.float32)
                # 估计姿态
                rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(marker_corners, self.marker_size, self.camera_matrix, self.dist_coeffs)
                
                if is_show:
                    # 绘制坐标轴
                    cv2.drawFrameAxes(result_image, self.camera_matrix, self.dist_coeffs, rvecs[0], tvecs[0], self.marker_size / 2)
                
                # 获取平移向量(米)
                translation = tvecs[0][0]
                translation[1] = -translation[1]  # y 和 z 反向
                translation[2] = -translation[2]
                
                translation = translation * self.position_rate   # 位置变化率

                # 将旋转向量转换为旋转矩阵
                rmat, _ = cv2.Rodrigues(rvecs[0])
                trans_x_180 = np.array([[1, 0, 0],
                                        [0, -1, 0],
                                        [0, 0, -1]])
                rmat = np.dot(trans_x_180, rmat)  # 转换为相机坐标系到aruco坐标系的姿态变换
                # 计算欧拉角
                euler_angles = self.rotation_matrix_to_euler_angles(rmat)
                #  平滑
                translation = self.k * self.position_pre + (1-self.k) * translation         # 位置平滑
                euler_angles = self.euler_angles_pre * self.k + (1-self.k) * euler_angles   # 姿态平滑
                
                self.euler_angles_pre = copy.deepcopy(euler_angles)
                self.position_pre = copy.deepcopy(translation)
                #  转成控制的实际旋转矩阵
                pos_result = np.vstack((np.hstack((self.euler_zyx_to_matrix(*euler_angles), translation.reshape(3, 1))), np.array([[0,0,0,1]])))
                
                if is_show:
                    # 在图像上显示位置和角度信息
                    pos_text = f"Position: X:{translation[0]:.3f} Y:{translation[1]:.3f} Z:{translation[2]:.3f}"
                    rot_text = f"Rotation: R:{euler_angles[0]*180/3.14:.1f} P:{euler_angles[1]*180/3.14:.1f} Y:{euler_angles[2]*180/3.14:.1f}"
                    cv2.putText(
                        result_image,
                        pos_text,
                        (center_point[0]-20, center_point[1] + 30),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.5,
                        (0, 0, 255),
                        1
                    )

                    cv2.putText(
                        result_image,
                        rot_text,
                        (center_point[0]-20, center_point[1] + 60),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.5,
                        (0, 0, 255),
                        1
                    )
                
                # 返回检测状态、图像和位姿结果
                return True, result_image, pos_result

            # 没有检测到aruco码
            return False, result_image, None


        except Exception as e:
            print(f"使用字典 {dict_names[dict_type]} 检测时出错: {e}")
            return False, result_image, None
 
 
    def rotation_matrix_to_euler_angles(self, R):
        """
        将旋转矩阵转换为欧拉角(弧度)
        参数:
            R: 3x3旋转矩阵
        返回:
            欧拉角 (roll, pitch, yaw) 弧度
        """
        # 检查是否是有效的旋转矩阵
        sy = math.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
    
        if sy > 1e-6:
            x = math.atan2(R[2, 1], R[2, 2])
            y = math.atan2(-R[2, 0], sy)
            z = math.atan2(R[1, 0], R[0, 0])
        else:
            x = math.atan2(-R[1, 2], R[1, 1])
            y = math.atan2(-R[2, 0], sy)
            z = 0
    
        return np.array([x, y, z])
    
    
    import numpy as np

    def euler_zyx_to_matrix(self, roll, pitch, yaw):
        """
        将 ZYX 欧拉角 (yaw, pitch, roll) 转换为旋转矩阵。
        参数:
            roll: 绕 X 轴的旋转角度(弧度)
            pitch: 绕 Y 轴的旋转角度(弧度)
            yaw: 绕 Z 轴的旋转角度(弧度)
        返回:
            3x3 的旋转矩阵 (numpy.ndarray)
        """
        cr = np.cos(roll)
        sr = np.sin(roll)
        cp = np.cos(pitch)
        sp = np.sin(pitch)
        cy = np.cos(yaw)
        sy = np.sin(yaw)

        R = np.array([
            [cy*cp, cy*sp*sr - sy*cr, cy*sp*cr + sy*sr],
            [sy*cp, sy*sp*sr + cy*cr, sy*sp*cr - cy*sr],
            [-sp,   cp*sr,            cp*cr]
        ])
        
        return R

    
    def close_cap(self):
        self.cap.release()
相关推荐
uzong3 分钟前
AI Agent 是什么,如何理解它,未来挑战和思考
人工智能·后端·架构
2401_895521345 分钟前
spring-ai 下载不了依赖spring-ai-openai-spring-boot-starter
java·人工智能·spring
冬奇Lab8 分钟前
从 Prompt 工程师到 Harness 工程师:AI 协作范式的三次进化
人工智能
jixinghuifu19 分钟前
理性权衡:手机系统更新,别盲目也别抗拒
人工智能·安全·智能手机
LJ979511122 分钟前
从被动救火到主动防御:Infoseek舆情监测系统的技术架构与实战拆解
人工智能
CareyWYR1 小时前
每周AI论文速递(260323-260327)
人工智能
guoji77881 小时前
安全与对齐的深层博弈:Gemini 3.1 Pro 安全护栏与对抗测试深度拆解
人工智能·安全
实在智能RPA1 小时前
实在 Agent 和通用大模型有什么不一样?深度拆解 AI Agent 的感知、决策与执行逻辑
人工智能·ai
独隅1 小时前
PyTorch 模型部署的 Docker 配置与性能调优深入指南
人工智能·pytorch·docker