机器人和摄像机的手眼标定问题分为两类构型:
- eye-to-hand,摄像机固定,与机器人基坐标系相对位置不变。
- eye-in-hand,摄像机安装在机器人末端,随着机器人一起移动。
所谓手眼系统,就是人眼睛看到一个东西的时候要让手去抓取,就需要大脑知道眼睛和手的坐标关系。如果把大脑比作B,把眼睛比作A,把手比作C,如果A和B的关系知道,B和C的关系知道,那么C和A的关系就知道了,也就是手和眼的坐标关系也就知道了。
相机知道的是像素坐标,机械手是空间坐标系,所以手眼标定就是得到像素坐标系和空间机械手坐标系的坐标转化关系。
在实际控制中,相机检测到目标在图像中的像素位置后,通过标定好的坐标转换矩阵将相机的像素坐标变换到机械手的空间坐标系中,然后根据机械手坐标系计算出各个电机该如何运动,从而控制机械手到达指定位置。这个过程中涉及到了图像标定,图像处理,运动学正逆解,手眼标定等。
常用的标定方法有:九点标定
九点标定:
九点标定直接建立相机和机械手之间的坐标变换关系。
让机械手的末端去走这就9个点得到在机器人坐标系中的坐标,同时还要用相机识别9个点得到像素坐标。这样就得到了9组对应的坐标。
由下面的式子可知至少需要3个点才能求出标定的矩阵。
(1)、标定,Halcon中进行9点标定的算子
%前面求出图像坐标 area_center(SortedRegions,Area,Row,Column) % Column_robot := [275,225,170,280,230,180,295,240,190] %机器人末端运动到9点的列坐标 Row_robot := [55,50,45,5,0,-5,-50,-50,-50] %机器人末端运动到9点的行坐标 vector_to_hom_mat2d(Row,Column,Row_robot,Column_robot,HomMat2D) %求解变换矩阵,HomMat2D是图像坐标和机械手坐标之间的关系
(2)、求解
affine_trans_point_2d(HomMat2D,Row2,Column2,Qx,Qy)
%由像素坐标和标定矩阵求出机器人基础坐标系中的坐标
一些特殊情况的解释:
有些情况中我们看到相机固定在一个地方,然后拍照找到目标,控制机械手去抓取,这种就很好理解。我们也叫做eye-to-hand
还有一种情况是相机固定在机械手上面,这种情况的标定过程实际上和相机和机械手分离的标定方法是一样的,因为相机拍照时,机械手会运动到相机标定的时候的位置,然后相机拍照,得到目标的坐标,再控制机械手,所以简单的相机固定在末端的手眼系统很多都是采用这种方法,标定的过程和手眼分离系统的标定是可以相同对待的。我们也叫做eye-in-hand
另有手眼标定的源码如下:
Halcon手眼标定例程注释 ——hand_eye_movingcam_calibration.hdev
* * This example explains how to use the hand eye calibration for the case where * the camera is attached to the robot tool and the calibration object * is stationary with respect to the robot. The robot positions the * camera with respect to the calibration plate. * In this case, the goal of the hand eye calibration is to determine two unknown poses: * - the pose of the robot base in the coordinate system * of the calibration object (CalObjInBasePose). * - the pose of the camera in the coordinate system of the * tool center point (ToolInCamPose). * Theoretically, as input the method needs at least 3 poses of the * calibration object in the camera coordinate system. * However, it is recommended to use at least 10 Poses. * The corresponding poses of the robot tool in the robot base coordinate system * (ToolInBasePose) changes for each calibration image, * because it describes the pose of the robot moving the camera. * The poses of the calibration object are obtained from images of the * calibration object recorded with the camera attached to the robot. * To obtain good calibration results, it its essential to position * the camera with respect to the calibration object so that the object appears * tilted in the image. * After the hand eye calibration, the computed transformations are * extracted and used to compute the pose of the calibration object in the * camera coordinate system. dev_update_off () * Directories with calibration images and data files *//设置标定图像初始图像名和初始参数文件名,便于遍历读取 ImageNameStart := \'3d_machine_vision/hand_eye/movingcam_calib3cm_\' DataNameStart := \'hand_eye/movingcam_\' *//设置标定图像采集次数 **//NumImages := 14 read_image (Image, \'E:/study/科研/机器视觉学习资料/halcon练习/标定实验/标定图像/1.jpg\') dev_close_window () get_image_size (Image, Width, Height) dev_open_window (0, 0, Width, Height, \'black\', WindowHandle) dev_set_line_width (2) dev_set_draw (\'margin\') dev_display (Image) set_display_font (WindowHandle, 14, \'mono\', \'true\', \'false\') ParamName := [\'color_0\',\'color_1\',\'color_2\',\'color_3\',\'color_4\',\'color_5\',\'color_6\',\'alpha_6\'] ParamValue := [\'red\',\'green\',\'blue\',\'red\',\'green\',\'blue\',\'white\',0.7] * //初始可视化机器人3D位姿中的标签矩阵,Labels for the visualized 3D object models. tuple_gen_const (7, \'\', Labels) Labels[0] := \'Robot\\'s Tool\' Labels[3] := \'Robot\\'s Base\' * //初始可视化机器人3D位姿中的操作指导信息 Instructions[0] := \'Rotate: Left button\' Instructions[1] := \'Zoom: Shift + left button\' Instructions[2] := \'Move: Ctrl + left button\' *//设置机器人三维位姿示意图中的坐标轴箭头样式 Set size for 3D visualization in [m] ArrowThickness := 0.005 ArrowLength := 0.05 *生成机器人3D模型,(Thickness of the arrows,Length of the arrows(输入);3D model of the robot\'s tool.3D model of the robot\'s Base(输出)) *//OM3DToolOrigin:= *//OM3DBase:= gen_robot_tool_and_base_object_model_3d (ArrowThickness, ArrowLength, OM3DToolOrigin, OM3DBase) * gen_robot_tool_and_base_object_model_3d (ArrowLength, ArrowLength, OM3DToolOrigin1, OM3DBase1) * Load the calibration plate description file. * Make sure that the file is in the current directory or * //设置标定板描述文件路径,便于后面读取标定板描述文件in HALCONROOT/calib, or use an absolute path. CalTabDescr:=\'E:/study/科研/机器视觉学习资料/halcon练习/标定实验/caltab.descr\' CalTabFile := CalTabDescr * 读取相机初始参数或参数文件Read the initial values for the internal camera parameters *read_cam_par (DataNameStart + \'start_campar.dat\', StartCamParam) *StartCamParam:=[\'area_scan_division\', 0.0117685, -2541.7, 7.32909e-006, 7.4e-006, 323.6, 245.555, 646, 493] StartCamParam:=[\'area_scan_polynomial\', 0.0058132, 11113.2, 9.66687e+008, -5.98123e+014, 0.279616, 0.535812, 5.60089e-006, 5.6e-006, 322.43, 235.729, 640, 480] * 建立手眼标定模型 Create the calibration model for the hand eye calibration * Create the calibration model for the hand eye calibration * 建立眼随手动的手眼标定模型(标定类型、相机数、标定板数、标定数据代号)where the calibration object is observed with a camera create_calib_data (\'hand_eye_moving_cam\', 1, 1, CalibDataID) * 设置标定相机类型(标定代号、相机代号、相机类型、相机初始参数)Set the camera type used set_calib_data_cam_param (CalibDataID, 0, [], StartCamParam) * 设定标定板信息Set the calibration object set_calib_data_calib_object (CalibDataID, 0, CalTabFile) * Start the loop over the calibration images *设定标定数据模型最优化方式Set the optimization method to be used * ?? set_calib_data (CalibDataID, \'model\', \'general\', \'optimization_method\', \'nonlinear\') disp_message (WindowHandle, \'The calibration data model was created\', \'window\', 12, 12, \'black\', \'true\') * disp_continue_message (WindowHandle, \'black\', \'true\') stop () dev_open_window (0, Width + 10, Width, Height, \'black\', WindowHandleR) set_display_font (WindowHandleR, 14, \'mono\', \'true\', \'false\') *遍历处理每个标定位姿的标定图形 list_files (\'E:/study/科研/机器视觉学习资料/halcon练习/标定实验/标定图像\', \'files\', ImageFiles) NumImages:=|ImageFiles| for I := 0 to NumImages - 1 by 1 dev_set_window (WindowHandle) dev_clear_window () *读取不同相机位姿获取的标定板图像 read_image (Image, ImageNameStart + I$\'02d\') dev_display (Image) * Search for the calibration plate, extract the marks and the * pose of it, and store the results in the calibration data * The poses are stored in the calibration data model for use by * the hand eye calibration and do not have to be set explicitly *搜索标定区域并识别标定轮廓和标定点信息 find_calib_object (Image, CalibDataID, 0, 0, I, [], []) *获取标定板轮廓信息 get_calib_data_observ_contours (Caltab, CalibDataID, \'caltab\', 0, 0, I) *获取标定板标定点信息 get_calib_data_observ_points (CalibDataID, 0, 0, I, RCoord, CCoord, Index, PoseForCalibrationPlate) * Visualize the extracted calibration marks and the estimated pose (coordinate system) dev_set_color (\'green\') dev_display (Image) dev_display (Caltab) dev_set_color (\'yellow\') *在标定点中心画十字标记 disp_cross (WindowHandle, RCoord, CCoord, 6, 0) dev_set_colored (3) *在标定板图像中显示标定板坐标系 disp_3d_coord_system (WindowHandle, StartCamParam, PoseForCalibrationPlate, 0.01) disp_message (WindowHandle, \'Extracting data from calibration image \' + (I + 1) + \' of \' + NumImages, \'window\', 12, 12, \'black\', \'true\') * 读取机器人模型的工具坐标系在机器人基坐标系中的变换矩阵,可以从机器人模型中得出。Read pose of tool in robot base coordinates (ToolInBasePose) read_pose (DataNameStart + \'robot_pose_\' + I$\'02d\' + \'.dat\', ToolInBasePose) if (I == 0) PoseIn := [-0.006,-0.296,12,178,2,270,0] else PoseIn := PoseOut endif *将工具初始坐标OM3DToolOrigin值通过ToolInBasePose即工具坐标在机器人基坐标中的位姿转换矩阵转换为工具在基坐标下的表示形式 OM3DTool rigid_trans_object_model_3d (OM3DToolOrigin, ToolInBasePose, OM3DTool) *create_pose (0.1, 0.2, 0.3, 40, 50, 60, \'R(p-T)\', \'abg\', \'point\', Pose) *(标准化3D交互显示)可视化交互显示机器人参数的3D模型坐标 visualize_object_model_3d (WindowHandleR, [OM3DTool,OM3DBase], [], PoseIn, ParamName, ParamValue, \'Position of robot tool coordinate system in robot base coordinate system\', Labels, Instructions, PoseOut) clear_object_model_3d (OM3DTool) * Set the pose tool in robot base coordinates in the calibration data model set_calib_data (CalibDataID, \'tool\', I, \'tool_in_base_pose\', ToolInBasePose) endfor dev_set_window (WindowHandleR) dev_close_window () disp_message (WindowHandle, \'All relevant data has been set in the calibration data model\', \'window\', 12, 12, \'black\', \'true\') disp_continue_message (WindowHandle, \'black\', \'true\') stop () * Check the input poses for consistency check_hand_eye_calibration_input_poses (CalibDataID, 0.05, 0.005, Warnings) if (|Warnings| != 0) * There were problem detected in the input poses. Inspect Warnings and * remove erroneous poses with remove_calib_data and remove_calib_data_observ. dev_inspect_ctrl (Warnings) stop () endif * * Perform the hand eye calibration and store the results to file * The calibration of the cameras is done internally prior * to the hand eye calibration dev_display (Image) disp_message (WindowHandle, \'Performing the hand-eye calibration\', \'window\', 12, 12, \'black\', \'true\') ***利用前面获取的标定数据进行手眼标定*** calibrate_hand_eye (CalibDataID, Errors) ***从手眼标定结果中获取所需的参数具体数值*** * Query the error of the camera calibration get_calib_data (CalibDataID, \'model\', \'general\', \'camera_calib_error\', CamCalibError) * Query the camera parameters and the poses get_calib_data (CalibDataID, \'camera\', 0, \'params\', CamParam) * Get poses computed by the hand eye calibration get_calib_data (CalibDataID, \'camera\', 0, \'tool_in_cam_pose\', ToolInCamPose) get_calib_data (CalibDataID, \'calib_obj\', 0, \'obj_in_base_pose\', CalObjInBasePose) * Get the plane in base coordinate system pose by translating the * CalObjInBasePose by the calibration object\'s thickness in the * z-direction. *//初始标定板位姿CalObjInBasePose在考虑标定板厚度后沿z轴平移变换的标定板在基坐标系中位姿PlaneInBasePose set_origin_pose (CalObjInBasePose, 0, 0, 0.005, PlaneInBasePose) dev_get_preferences (\'suppress_handled_exceptions_dlg\', PreferenceValue) dev_set_preferences (\'suppress_handled_exceptions_dlg\', \'true\') try * Handle situation where user does not have the permission * to write in the current directory. * * Store the camera parameters to file write_cam_par (CamParam, DataNameStart + \'final_campar.dat\') * Save the hand eye calibration results to file write_pose (ToolInCamPose, DataNameStart + \'final_pose_cam_tool.dat\') write_pose (CalObjInBasePose, DataNameStart + \'final_pose_base_calplate.dat\') write_pose (PlaneInBasePose, DataNameStart + \'final_pose_base_plane.dat\') catch (Exception) * do nothing endtry dev_set_preferences (\'suppress_handled_exceptions_dlg\', PreferenceValue) dev_display (Image) * Display calibration errors disp_results (WindowHandle, CamCalibError, Errors) * disp_continue_message (WindowHandle, \'black\', \'true\') stop () *?? For the given camera, get the corresponding pose indices and calibration object indices *//query_calib_data_observ_indices( : : CalibDataID, ItemType, ItemIdx : Index1, Index2) *// Index1 returns a list of calibration object indices and Index2 returns a list of pose indices. *//Each pair [Index1[I],Index2[I]] represents calibration object pose that are \'observed\' by camera ItemIdx. query_calib_data_observ_indices (CalibDataID, \'camera\', 0, CalibObjIdx, PoseIds) * Compute the pose of the calibration object in the camera coordinate * system via calibrated poses and the ToolInBasePose and visualize it. * Set sizes for 3D visualization in [m] CameraSize := 0.05 CameraConeLength := 0.3 get_calib_data (CalibDataID, \'calib_obj\', 0, \'x\', PX) get_calib_data (CalibDataID, \'calib_obj\', 0, \'y\', PY) get_calib_data (CalibDataID, \'calib_obj\', 0, \'z\', PZ) *//建立标定板标定点的点云 gen_object_model_3d_from_points (PX, PY, PZ, OM3DObjectOrig) *//将初始标定板位姿OM3DObjectOrig通过标定板与基坐标系之间的转换关系CalObjInBasePose(从标定数据中获得)转换到基坐标系中表示 OM3DObject rigid_trans_object_model_3d (OM3DObjectOrig, CalObjInBasePose, OM3DObject) clear_object_model_3d (OM3DObjectOrig) dev_open_window (0, Width + 10, Width, Height, \'black\', WindowHandleR) set_display_font (WindowHandleR, 14, \'mono\', \'true\', \'false\') ParamName := [\'color_0\',\'color_1\',\'color_2\',\'color_3\',\'color_4\',\'color_5\',\'color_6\',\'color_7\',\'alpha_7\',\'color_8\',\'color_9\',\'color_10\',\'alpha_8\',\'alpha_9\',\'alpha_10\',\'point_size\'] ParamValue := [\'red\',\'red\',\'green\',\'blue\',\'red\',\'green\',\'blue\',\'white\',0.7,\'magenta\',\'yellow\',\'white\',0.5,0.5,0.5,5] * Labels for the visualized 3D object models. tuple_gen_const (11, \'\', Labels) Labels[0] := \'Calibration Object\' Labels[1] := \'Robot\\'s Tool\' Labels[4] := \'Robot\\'s Base\' Labels[8] := \'Camera\' for I := 0 to NumImages - 1 by 1 dev_set_window (WindowHandle) dev_clear_window () read_image (Image, ImageNameStart + I$\'02d\') dev_display (Image) * Obtain the pose of the tool in robot base coordinates used in the calibration. * The index corresponds to the index of the pose of the observation object. get_calib_data (CalibDataID, \'tool\', PoseIds[I], \'tool_in_base_pose\', ToolInBasePose) * Compute the pose of the calibration object relative to the camera calc_calplate_pose_movingcam (CalObjInBasePose, ToolInCamPose, ToolInBasePose, CalObjInCamPose) * Display the coordinate system dev_set_colored (3) disp_3d_coord_system (WindowHandle, CamParam, CalObjInCamPose, 0.01) Message := \'Using the calibration results to display \' Message[1] := \'the coordinate system in image \' + (I + 1) + \' of \' + NumImages disp_message (WindowHandle, Message, \'window\', 12, 12, \'black\', \'true\') gen_camera_and_tool_moving_cam_object_model_3d (ToolInCamPose, ToolInBasePose, CameraSize, CameraConeLength, OM3DToolOrigin, CamParam, OM3DCamera, OM3DTool) if (I == 0) PoseIn := [-0.006,-0.296,12,178,2,270,0] else PoseIn := PoseOut endif visualize_object_model_3d (WindowHandleR, [OM3DObject,OM3DTool,OM3DBase,OM3DCamera], [], PoseIn, ParamName, ParamValue, [], Labels, Instructions, PoseOut) clear_object_model_3d (OM3DTool) clear_object_model_3d (OM3DCamera) endfor * Clear the data model clear_calib_data (CalibDataID) clear_object_model_3d (OM3DObject) clear_object_model_3d (OM3DToolOrigin) clear_object_model_3d (OM3DBase) dev_set_window (WindowHandleR) dev_close_window () * * After the hand-eye calibration the computed pose * ToolInCamPose can be used in robotic grasping applications. * To grasp an object with the robot, typically, its pose * with respect to the camera is determined (which * is simulated here by setting the object\'s pose to the * pose of the calibration object) ObjInCamPose := CalObjInCamPose * If the tool coordinate system is placed at the gripper * and a detected object ObjInCamPose shall be grasped * (here the calibration object), * the pose of the detected object relative * to the robot base coordinate system has to be computed. pose_invert (ToolInCamPose, CamInToolPose) pose_compose (ToolInBasePose, CamInToolPose, CamInBasePose) pose_compose (CamInBasePose, ObjInCamPose, ObjInBasePose)