import os import re import time import signal import cv2 import subprocess import numpy as np import pyrealsense2 as rs from scipy.spatial.transform import Rotation as R from get_chessboard_position import init_realsense, get_closest_red_point import sys # 把.so所在目录加入 Python 路径 sys.path.append("/home/lgv/cmvr/cmvr-es/cmake-build-debug/example") # 导入模块 from robot_wrapper import Robot def get_pose(joint_positions, base_link="PELVIS_S", target_link="R_FINGER_TIP", exec_path="/home/lgv/cmvr/cmvr-es/cmake-build-debug/example/solve_fk"): # ----------- 1. 构造命令行参数 ----------- joint_str = [str(j) for j in joint_positions] cmd = [exec_path, base_link, target_link, *joint_str] # ----------- 2. 运行并捕获输出 ----------- res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, text=True) # ----------- 3. 解析 4×4 变换矩阵 ----------- # 定位起始行(含 “变换矩阵T” 或 “T:”) lines = res.stdout.splitlines() start_idx = next((i for i, l in enumerate(lines) if re.search(r"变换矩阵|T\s*:", l)), None) if start_idx is None or start_idx + 4 >= len(lines): raise RuntimeError("未在 solve_fk 输出中找到 4×4 变换矩阵:\n" + res.stdout) try: mat = np.array([[float(x) for x in lines[start_idx + 1 + r].split()] for r in range(4)], dtype=np.float64) if mat.shape != (4, 4): raise ValueError except Exception: raise RuntimeError("矩阵解析失败,原始输出:\n" + res.stdout) return mat def solve_ik(x, y, z, rx, ry, rz, base_link="PELVIS_S", target_link="R_WRIST_R_S", exe_path= "/home/lgv/cmvr/cmvr-es/cmake-build-debug/example/solve_ik"): cmd = [exe_path, base_link, target_link, f"{x}", f"{y}", f"{z}", f"{rx}", f"{ry}", f"{rz}"] print(" ".join(cmd)) res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=True) lines = res.stdout.splitlines() success_line = next((l for l in lines if "[IK Solve]" in l), "") success = "success=true" in success_line.lower() if not success: return False, None, None def _parse(tag): pats = rf"^{tag}\s*:\s*(.+)$" for l in lines: m = re.match(pats, l.strip()) if m: return [float(v) for v in m.group(1).split()] raise RuntimeError(f"success=true 但未找到 {tag}: 行!\n{res.stdout}") left_q = _parse("left") right_q = _parse("right") return True, left_q, right_q def moveJ(q, side='right'): cmd = ["moveJ" ,"one" , f"{side}", f"{q[0]}", f"{q[1]}", f"{q[2]}", f"{q[3]}", f"{q[4]}", f"{q[5]}", f"{q[6]}"] res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=True) print("movej success") def rotation_to_degree(mat): pitch = -np.arcsin(mat[2, 0]) if np.abs(np.cos(pitch)) > 1e-6: # 非奇异 roll = np.arctan2(mat[2, 1], mat[2, 2]) yaw = np.arctan2(mat[1, 0], mat[0, 0]) else: # gimbal lock roll = 0.0 yaw = np.arctan2(-mat[0, 1], mat[1, 1]) return yaw, pitch, roll def black_point_position(color_frame, depth_frame, depth_intr, vis_path=None, min_radius=10, avg_window=3): """ 检测白底黑圆的圆心坐标,返回相机系 (X, Y, Z) [m] 参数 ---- color_frame / depth_frame : 对齐后的 RealSense frame depth_intr : 深度流 intrinsics (rs.intrinsics) vis_path : 若给定,则保存可视化图片 min_radius : HoughCircles/轮廓的最小半径,像素 avg_window : 深度均值窗口半径(像素) """ color_img = np.asanyarray(color_frame.get_data()).copy() gray = cv2.cvtColor(color_img, cv2.COLOR_BGR2GRAY) # 1. 二值化(寻找黑色区域) # Otsu 自动阈值 + 取反 => 黑圆为白,背景为黑 _, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 2. 轮廓检测,取面积最大的圆形候选 cnts, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not cnts: raise RuntimeError("未检测到任何黑色区域") # 取最大面积 cnt = max(cnts, key=cv2.contourArea) (u, v), radius = cv2.minEnclosingCircle(cnt) if radius < min_radius: raise RuntimeError(f"检测到圆半径过小 ({radius:.1f}px),请检查 min_radius 设置或图像质量") # 3. 取邻域深度中值 width, height = depth_frame.get_width(), depth_frame.get_height() depths = [ depth_frame.get_distance(int(round(u + du)), int(round(v + dv))) for du in range(-avg_window, avg_window + 1) for dv in range(-avg_window, avg_window + 1) if 0 <= int(round(u + du)) < width and 0 <= int(round(v + dv)) < height ] depths = [d for d in depths if d > 0] if not depths: raise RuntimeError("圆心处深度无效 (0)") depth = float(np.median(depths)) # 4. 像素 -> 相机坐标 x, y, z = rs.rs2_deproject_pixel_to_point( depth_intr, [u, v], depth ) pos = np.array([x, y, z], dtype=np.float32) # 5. 可视化 if vis_path is not None: cx, cy = depth_intr.ppx, depth_intr.ppy cv2.drawMarker(color_img, (int(cx), int(cy)), (0, 255, 0), markerType=cv2.MARKER_CROSS, markerSize=20, thickness=2) cv2.circle(color_img, (int(u), int(v)), int(radius), (0, 0, 255), 2) cv2.circle(color_img, (int(u), int(v)), 5, (0, 0, 255), -1) cv2.imwrite(vis_path, color_img) print(f"✓ 已保存标记图到 {vis_path}") return pos def red_point_position(color_frame, depth_frame, depth_intr, vis_path=None, min_radius=3, avg_window=3): """ 检测白底红圆的圆心坐标,返回相机系 (X,Y,Z) [m] """ # --- 1. 取彩色帧 ---- color_img = np.asanyarray(color_frame.get_data()).copy() hsv = cv2.cvtColor(color_img, cv2.COLOR_BGR2HSV) # --- 2. 阈值分割:红色有两个 Hue 区间 (0-10)∪(170-180) --- lower_red1 = np.array([0, 100, 100]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([170, 100, 100]) upper_red2 = np.array([180, 255, 255]) mask1 = cv2.inRange(hsv, lower_red1, upper_red1) mask2 = cv2.inRange(hsv, lower_red2, upper_red2) mask = cv2.bitwise_or(mask1, mask2) # 可选:形态学开闭运算去噪 kernel = np.ones((5,5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # --- 3. 轮廓取最大圆 --- cnts, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not cnts: cv2.imwrite(vis_path, color_img) raise RuntimeError("未检测到任何红色区域") cnt = max(cnts, key=cv2.contourArea) (u, v), radius = cv2.minEnclosingCircle(cnt) if radius < min_radius: raise RuntimeError(f"检测到圆半径过小 ({radius:.1f}px),请检查 min_radius 或图像质量") # --- 4. 深度中值 --- width, height = depth_frame.get_width(), depth_frame.get_height() depths = [ depth_frame.get_distance(int(round(u + du)), int(round(v + dv))) for du in range(-avg_window, avg_window + 1) for dv in range(-avg_window, avg_window + 1) if 0 <= int(round(u + du)) < width and 0 <= int(round(v + dv)) < height ] depths = [d for d in depths if d > 0] if not depths: raise RuntimeError("圆心处深度无效 (0)") depth = float(np.median(depths)) # --- 5. 反投影到 3-D --- x, y, z = rs.rs2_deproject_pixel_to_point(depth_intr, [u, v], depth) pos = np.array([x, y, z], dtype=np.float32) # --- 6. 可视化保存 --- if vis_path is not None: cx, cy = depth_intr.ppx, depth_intr.ppy cv2.drawMarker(color_img, (int(cx), int(cy)), (0, 255, 0), markerType=cv2.MARKER_CROSS, markerSize=20, thickness=2) cv2.circle(color_img, (int(u), int(v)), int(radius), (255, 0, 0), 2) # 蓝圈标红圆 cv2.circle(color_img, (int(u), int(v)), 5, (255, 0, 0), -1) cv2.imwrite(vis_path, color_img) print(f"✓ 已保存标记图到 {vis_path}") return pos # if __name__ == "__main__": # # 初始化机器人(传入配置文件路径和机器人名称) # # pipeline, align, depth_intr = start_pipeline("243122075614") # robot = Robot("/home/lgv/cmvr/cmvr-es/config/cabin_robot.xml", "hc01") # js = robot.getJointQ('right') # T_base_ee = get_pose(js, base_link="PELVIS_S", target_link="R_WRIST_R_S") # T_base_cam = get_pose(js, base_link="PELVIS_S", target_link="R_FINGER_TIP") # P_base_cam = T_base_cam[:3, 3] # # color_f, depth_f = get_aligned_frames(pipeline, align) # # P_cam_target = black_point_position(color_f, depth_f, depth_intr, vis_path="black_point.png") # P_cam_target = red_point_position(color_f, depth_f, depth_intr, vis_path="red_point.png") # # P_base_target = np.array([ # P_base_cam[0] + P_cam_target[2], # P_base_cam[1] + P_cam_target[0], # P_base_cam[2] - P_cam_target[1], # ]) # print("P_base_target:", P_base_target) # # P_base_tool = P_base_target - np.array([0.45, 0, 0]) # T_base_tool = T_base_ee # T_base_tool[:3, 3] = P_base_tool # # print("T_base_tool:", T_base_tool) # # target_x, target_y, target_z = T_base_tool[0, 3], T_base_tool[1, 3], T_base_tool[2, 3] # target_rx, target_ry, target_rz =R.from_matrix(T_base_tool[:3, :3]).as_euler('xyz', degrees=True) # # ok, lq, rq = solve_ik(target_x, target_y, target_z, target_rx, target_ry, target_rz) # if not ok: # print("solve ik failed") # exit(-1) # # moveJ(rq) # # # try: # # while True: # # js = get_joint_position('right') # # T_base_ee = get_pose(js, base_link="PELVIS_S", target_link="R_WRIST_R_S") # # T_base_cam = get_pose(js, base_link="PELVIS_S", target_link="R_FINGER_TIP") # # P_base_cam = T_base_cam[:3, 3] # # # # color_f, depth_f = get_aligned_frames(pipeline, align) # # # P_cam_target = black_point_position(color_f, depth_f, depth_intr, vis_path="black_point.png") # # P_cam_target = red_point_position(color_f, depth_f, depth_intr, vis_path="red_point.png") # # # # P_base_target = np.array([ # # P_base_cam[0] + P_cam_target[2], # # P_base_cam[1] + P_cam_target[0], # # P_base_cam[2] - P_cam_target[1], # # ]) # # print("P_base_target:", P_base_target) # # # # P_base_tool = P_base_target - np.array([0.3, 0, 0]) # # T_base_tool = T_base_ee # # T_base_tool[:3, 3] = P_base_tool # # # # print("T_base_tool:", T_base_tool) # # # # target_x, target_y, target_z = T_base_tool[0, 3], T_base_tool[1, 3], T_base_tool[2, 3] # # target_rx, target_ry, target_rz =R.from_matrix(T_base_tool[:3, :3]).as_euler('xyz', degrees=True) # # # # ok, lq, rq = solve_ik(target_x, target_y, target_z, target_rx, target_ry, target_rz) # # if not ok: # # print("solve ik failed") # # exit(-1) # # # moveJ(rq) # # except Exception as e: # # print(e) # ========== Ctrl+C 处理 ========== def signal_handler(sig, frame): print("\n收到 Ctrl+C,准备退出...") raise SystemExit signal.signal(signal.SIGINT, signal_handler) def rt_to_transform(R, t): """拼接旋转矩阵和平移向量为齐次矩阵""" T = np.eye(4) T[:3, :3] = R T[:3, 3] = np.squeeze(t) return T def transform_point(T, P): """用齐次变换矩阵 T (4x4) 把点 P(3,) 转换到新坐标系""" P_h = np.append(P, 1) # [x, y, z, 1] P_new = T @ P_h return P_new[:3] def compute_point_in_base(T_base_ee, R_ee_cam, t_ee_cam, P_cam_target): """ 已知: T_base_ee : 基座->末端 (4x4) R_ee_cam : 末端->相机的旋转 (3x3) t_ee_cam : 末端->相机的平移 (3,) P_cam_target : 目标点在相机下的坐标 (3,) 返回: P_base_target : 目标点在基座下的坐标 (3,) """ # 拼接 T_ee_cam T_ee_cam = rt_to_transform(R_ee_cam, t_ee_cam) # 得到相机在 base 下的位姿 T_base_cam = T_base_ee @ T_ee_cam # 把目标点从相机系变换到 base 系 P_base_target = transform_point(T_base_cam, P_cam_target) return P_base_target if __name__ == "__main__": pipeline, align = init_realsense() try: # 初始化机器人 robot = Robot("/home/lgv/cmvr/cmvr-es/config/cabin_robot.xml", "hc01") # robot.moveJ("right", [0.00203898, 1.34062, 0.0,0.322261, 0.0,-0.000210733, -0.0942364]) robot.moveJ("right", [-0.344938, 0.935147, 2.27031,1.68959, -2.32841,0.460145, 0.300996]) js = robot.getJointQ('right') print("js = ") print(js) T_base_ee = get_pose(js, base_link="PELVIS_S", target_link="R_WRIST_R_S") # T_base_cam = get_pose(js, base_link="PELVIS_S", target_link="R_FINGER_TIP") # P_base_cam = T_base_cam[:3, 3] print("T_base_ee =\n", T_base_ee) # print("T_ee_cam =\n", T_ee_cam) # print("T_base_cam =\n", T_base_cam) P_cam_target = get_closest_red_point(pipeline, align, vis_path="red_point.png") # P_cam_target = red_point_position(color_f, depth_f, depth_intr, vis_path="red_point.png") print("P_cam_target (camera frame) =", P_cam_target) # T_cam_target = np.eye(4) # T_cam_target[:3, 3] = P_cam_target # T_base_target = T_base_cam @ T_cam_target # P_base_target = T_base_target[:3, 3] # R_ee_cam = np.array([ # [-1, 0, 0], # [ 0, 0, -1], # [ 0, -1, 0] # ]) # t_ee_cam = np.array([-0.01212, -0.17655, 0.07506]) R_ee_cam = np.array( [[-0.99732744 , 0.02734315 , 0.0677519 ], [-0.06713675 ,0.0228221 , -0.99748274], [-0.02882056,-0.99936555, -0.02092538]]) t_ee_cam = np.array([-0.00078482, -0.17419722, 0.06796275]) # t_ee_cam = np.array([-0.05212, -0.17419722, 0.06796275]) # R_ee_cam = np.array( [[-0.99486349, 0.07846371 , 0.06395369], # [-0.05884529 ,0.06577685, -0.9960977 ], # [-0.08236419 ,-0.99474462 ,-0.06082177]]) # # t_ee_cam = np.array([-0.00797824, -0.17640328, 0.07459845]) P_base_target = compute_point_in_base(T_base_ee, R_ee_cam, t_ee_cam, P_cam_target) print("P_base_target (base frame) =", P_base_target) # 位置:直接用 P_base_target target_x, target_y, target_z = P_base_target - np.array([0.45, 0, 0]) target_rx, target_ry, target_rz =R.from_matrix(T_base_ee[:3, :3]).as_euler('xyz', degrees=True) # 调 IK ok, lq, rq = solve_ik(target_x, target_y, target_z, target_rx, target_ry, target_rz) print("rq =", rq) if ok: robot.moveJ("right", rq) else: print("solve ik failed") print("已到达目标点,按 Ctrl+C 回到初始位姿...") while True: time.sleep(1) except SystemExit: print("安全退出程序...") finally: robot.moveJ("right", [0.00203898, 1.34062, 0.0,0.322261, 0.0,-0.000210733, -0.0942364]) pipeline.stop() align = None depth_intr = None # # if __name__ == "__main__": # # try: # # 初始化机器人 # robot = Robot("/home/lgv/cmvr/cmvr-es/config/cabin_robot.xml", "hc01") # # # robot.moveJ("right", [0.00203898, 1.34062, 0.0,0.322261, 0.0,-0.000210733, -0.0942364]) # # robot.moveJ("right", [-0.344938, 0.935147, 2.27031,1.68959, -2.32841,0.460145, 0.300996]) # js = robot.getJointQ('right') # print("js = ") # print(js) # # # print("已到达目标点,按 Ctrl+C 回到初始位姿...") # while True: # time.sleep(1) # except SystemExit: # print("安全退出程序...") # # finally: # robot.moveJ("right", [0.00203898, 1.34062, 0.0,0.322261, 0.0,-0.000210733, -0.0942364])