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()
相关推荐
智行合一科技3 分钟前
中国互联网营销发展报告发布,GEO与代理式AI成焦点
人工智能
禁默8 分钟前
论文解读与思考:基于场所地图的标识感知机器人探索方法及工程参考的价值
人工智能·深度学习·算法·机器人
青衫烟雨9 分钟前
DeepSeek 崩了 5 小时:你的 AI 应用为什么跟着一起挂?
人工智能·架构
loulanyue_10 分钟前
不改一个参数底座的智能跃迁:智谱GLM-5.3极致后训练与Slime长程强化学习——读智谱AI技术报告与《Lau博士的云组会》深度解析
人工智能
墨云安全12 分钟前
AI 漏洞挖掘平台怎么评估?2026 年技术架构与选型要点
网络·人工智能·网络安全·架构·渗透测试
Pin_Qing13 分钟前
同一天,谷歌半价卖旗舰,阶跃翻倍收缓存
大数据·开发语言·人工智能·程序人生·缓存
云票14 分钟前
从技术视角看AI采购:多智能体、流程重设计与自动化的落地边界
运维·人工智能·自动化
Zentceh14 分钟前
PixelClean 这类 AI 降噪算法是怎么工作的?从 RAW 域到 GAN 色彩重建的技术拆解
图像处理·人工智能·科技·算法·计算机视觉·车载系统·智能硬件
2601_9622035115 分钟前
2026主流AI办公智能体对比:KuWork、安捷AI、bit-Agent、青虹AI、Team Claw怎么选
人工智能
Zwawa17 分钟前
黑盒与白盒:人是怎么把世界装进口袋的
人工智能·嵌入式