文章目录
- 1,介绍。
- 2,技术原理
- 3,类型。
- 3.1,直射式
- 3.2,斜射式
- 3.3,两种三角位移传感器特性的比较
- 4,什么是光片?
- 5,主要的算子。
- 1,create_sheet_of_light_model
- 2,measure_profile_sheet_of_light
- 3,get_sheet_of_light_result
- 6,三角标定测量代码详解。
- 6.1, 相机标定参数预设置。
- 6.2,标定相机。
- 6.3,计算LightPanelPose(光平面的位姿)。
- 6.4,计算MovementPose。
- 6.5,激光三角应用。
- 7,完整代码。
1,介绍。
三角激光测量是一种位移测量方法,其最大的优点是非接触性测量。通过三维激光扫描获取的图像纹理丰富,分辨率高,具有更好的深度和范围信息,能更好的满足微小产品的视觉检测需求,故在工业应用和基础科学研究中被广泛使用,对微小产品表面平面度测量技术的研究就显得尤为重要。
2,技术原理
激光器发出的激光照射到被测物体表面,激光在被测物表面形成反射,返回到成像器,从而计算出物体的高度。由于入射光和反射光构成一个三角形,所以这种方法被称为三角测量法。如果激光线投射到物体表面的高度不同,则发光线条不会是一条直线,而是一条表现物体表面高度轮廓的线。通过这条轮廓线,就可以得到物体表面的高度差。
视差图中的每一行存储一条轮廓线的值,这里的相机必须是固定的,这样每一行扫描到的轮廓线才能和视差图中对应的行平行。而被测物体应当是运动,这样才能获得完整的轮廓线。如果系统未经过校准,则不会返回点在世界坐标系的三维坐标,但是仍然可以得到视差图像,以及测量结果的置信分数。注意,这里的视差图像有所不同,双目视觉中的视差图像体现了左右图中对应的像素灰度值差,而片光测量结果的视差图中保存的是被检测到的轮廓线的子像素。
3,类型。
按照发射角度的不同,激光三角法按入射光线与被测对象表面法线方向所成的角度分为直射式与斜射式。
3.1,直射式
激光器发出的光线,经会聚透镜聚焦后垂直入射到被测物体表面,物体移动或表面变化导致入射光点沿入射光轴移动。接收透镜接收来自入射光点处的散射光,并将其成像在光点位置探测器(如PSD,CCD)敏感面上,但由于传感器激光光束与被测面垂直,因此只有一个准确的调焦位置,其余位置的像都处于不同程度的离焦状态。
若光点在成像面上的位移为x`,利用相似三角形各边之间的比例关系,可以求出背侧面的位移:
x = a x ‘ s i n θ 2 b s i n θ − x ‘ s i n ( θ 1 − θ 2 ) x=\frac{ax^`sin\theta_2}{bsin\theta-x^`sin(\theta_1-\theta_2)} x=bsinθ−x‘sin(θ1−θ2)ax‘sinθ2
式中,a为激光束光轴和接收光轴的交点到接收透镜前主面的距离;b为接收透镜后主面到成像面中心点的距离; θ 1 \theta_1 θ1为激光束光轴与接收透镜光轴之间的夹角; θ 2 \theta_2 θ2为探测器与接收透镜光轴之间的夹角。
3.2,斜射式
激光器发出的光和被测面的法线方向成一定角度入射到被测面上,同样用接收透镜接收光点在被测面的散射光或反射光。
若光点的像在探测器敏感面上移动x`,利用相似三角形的比例关系,则物体表面沿法线方向的移动距离为:
x = a x ‘ s i n θ 3 c o s θ 1 b s i n ( θ 1 + θ 2 ) − x ‘ s i n ( θ 1 + θ 2 + θ 3 ) x=\frac{ax^`sin\theta_3cos\theta_1}{bsin(\theta_1+\theta_2)-x^`sin(\theta_1+\theta_2+\theta_3)} x=bsin(θ1+θ2)−x‘sin(θ1+θ2+θ3)ax‘sinθ3cosθ1
式中, θ 1 \theta_1 θ1为激光束光轴与被测面的法线夹角; θ 2 \theta_2 θ2为成像透镜与被测面的法线夹角; θ 3 \theta_3 θ3为探测器光轴与成像透镜光轴之间的夹角。
3.3,两种三角位移传感器特性的比较
基于三角测量法的传感器称为激光三角位移传感器,具体可以分为直射式激光三角位移传感器和斜射式激光三角位移传感器。这两种传感器都可以对被测面进行高精度、高速度的非接触式测量,但比较起来有以下几点区别:
- 斜射式可以接收来自被测物体的正反射光,比较适合测量表面接近镜面的物体。直射式由于其接收散射光的特点,适合于测量散射性能好的表面。
- 直射式光斑较小,光强集中,不会因被测面不垂直而扩大光斑,而且一般体积比较小。斜射式传感器分辨率高于直射式,但他的测量范围较小,体积较大。
4,什么是光片?
光片技术的基本思想是将一条细的发光直线投影到要重建的物体的表面上,然后用相机对投影线进行成像,光片技术也称线结构光。
实物图像
激光线的投影构成了一个称为光平面或光片的平面。相机的光轴与光平面形成一个角度 α \alpha α,称为三角测量角。激光线与相机视图之间的交点取决于被测物体的高度。因此,如果激光线投射到的物体的高度不同,则该线不会称为直线,而是表示物体的轮廓。
5,主要的算子。
1,create_sheet_of_light_model
用于光片技术(Sheet-of-Light,亦称之为线性结构光)3D测量的关键算子,主要用于通过激光三角测量技术重建物体表面轮廓。该算子通过分析激光线在物体表面的变形来获取高度信息,适用于工业检测、逆向工程等领域。
核心参数:
参数 | 类型 | 说明 | 典型值 |
---|---|---|---|
ProfileRegion | 输入 | 包含处理轮廓的图像ROI区域 | 最小外接矩形 |
GenParamName | 输入 | 可调整的通用参数名称 | 'min_gray’等13种选项 |
GenParamValue | 输入 | 对应参数的值 | 默认50 |
SheetOfLightModelID | 输出 | 光片模型句柄 | 用于后续操作 |
GenParamName可选值:
-
‘calibration’:校准相关参数
-
‘method’:轮廓提取方法
-
‘min_gray’:定义轮廓提取的最小灰度阈值,低于此值的像素将被忽略(默认50)。
-
‘score_type’:评分类型
-
‘scale_x/y/z’:各轴向缩放因子
-
‘num_profiles’:指定处理的轮廓数量,影响多帧平均和运动补偿效果。静态测量:设置为1(单帧处理),运动物体:根据运动速度设置(如传送带场景常用50-500)
标定要求与area_scan_polynomial模型
线结构光系统标定必须使用area_scan_polynomial
相机模型,原因如下:
- 高精度需求:多项式模型使用K1-K3三个参数描述径向畸变,P1-P2两个参数描述切向畸变,比division单参数模型精度更高
- 畸变校正:激光三角测量对镜头畸变敏感,polynomial模型能更好校正广角镜头的桶形/枕形畸变
- 系统架构:标定过程需要相机内外参和激光平面参数,多项式模型提供更完整的参数空间
2,measure_profile_sheet_of_light
光片测量技术(Sheet-of-Light)中的关键操作,主要用于激光条纹剖面数据的精确提取与分析。其核心功能与特性如下:
一、功能定位
-
核心作用
- 从光片模型生成的图像中提取激光条纹的亚像素级剖面数据。
- 输出剖面点的坐标和灰度值,用于后续三维重建或缺陷检测。
-
技术关联
- 需配合
create_sheet_of_light_model
创建的光片模型句柄使用。
- 需配合
二、参数解析
参数类型 | 关键参数 | 作用说明 |
---|---|---|
输入参数 | SheetOfLightHandle | 已创建的光片模型句柄2 |
ProfileImage | 包含激光条纹的输入图像 | |
输出参数 | Profile | 提取的剖面点坐标序列 |
GrayValues | 对应点的灰度值数组 |
三、典型应用场景
-
工业三维测量
- 金属零件表面轮廓重建(如连接杆的深度测量)
- 注塑模具孔洞检测(通过剖面灰度突变分析)
-
流程示例
halconCopy Code* 创建光片模型 create_sheet_of_light_model(ProfileRegion, ['min_gray'], [80], SheetOfLightHandle)* 处理单帧图像 read_image(ProfileImage, 'laser_profile_01.png') measure_profile_sheet_of_light(ProfileImage, SheetOfLightHandle, Profile, GrayValues)
四、性能优化建议
- 参数调优
- 通过
set_sheet_of_light_param
调整'profile_width'
(推荐3-10像素)提升条纹定位精度 - 设置
'min_gray'
过滤环境光干扰
- 通过
- 硬件协同
- 使用高动态范围相机避免激光过曝
- 标定阶段需保证激光平面与相机视角夹角>30°
该算子是实现激光三角测量三维重建的核心环节,其精度直接影响最终测量结果。
3,get_sheet_of_light_result
是光片测量技术(Sheet-of-Light)的核心数据提取接口,用于获取线结构光三维测量的标定或未标定结果。其功能特性与使用方法如下:
一、核心功能
-
数据输出类型
- 支持5种结果类型通过
参数指定:'disparity'
:未标定的视差图像(亚像素行坐标)'score'
:条纹匹配质量评分(依赖'score_type'
参数)'x'
/'y'
/'z'
:标定后的三维坐标点云
- 支持5种结果类型通过
二、参数详解
参数类别 | 参数名 | 作用说明 |
---|---|---|
输入参数 | SheetOfLightModelID | 已初始化的光片模型句柄 |
ResultName | 指定输出数据类型(默认'disparity' ) | |
输出参数 | ResultValue | 根据ResultName 返回对应数据 |
三、典型应用场景
- 工业三维检测
- 使用
'z'
结果进行零件高度公差验证 - 通过
'score'
分析激光条纹质量,识别表面缺陷
- 使用
- 代码示例
halconCopy Code* 获取标定后的三维坐标
get_sheet_of_light_result(ResultZ, SheetOfLightHandle, 'z')
* 可视化Z轴深度图
dev_display(ResultZ)
6,三角标定测量代码详解。
6.1, 相机标定参数预设置。
dev_update_off ()
dev_close_window ()
read_image (ProfileImage, 'sheet_of_light/connection_rod_001.png')
get_image_size (ProfileImage, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
dev_set_draw ('margin')
dev_set_line_width (3)
dev_set_color ('lime green')
dev_set_lut ('default')
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
*
* -------
* Part 1: 执行相机的标定
* -------
*
* 1.1,默认的相机内参
StartParameters := [0.0125,0.0,0.0,0.0,0.0,0.0,0.000006,0.000006,376.0,120.0,752,240]* 1.2,标定板描述文件
* caltab_30mm.descr':Distance between mark centers [meter]: 0.00375
CalTabDescription := 'caltab_30mm.descr'* 1.3,标定板的厚度(单位:m)
CalTabThickness := .00063* 1.4,标定板图像数量
NumCalibImages := 20
*
* Initialize a calibration data model
create_calib_data ('calibration_object', 1, 1, CalibDataID)* 1.4,设置标定参数
* 'area_scan_polynomial':复杂畸变、需要亚像素级精度。至少需要15张标定图像。
* 'division':至少需要5张标定图像。
* 线结构光系统标定必须使用`area_scan_polynomial`相机模型
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_polynomial', StartParameters)
set_calib_data_calib_object (CalibDataID, 0, CalTabDescription)
释疑点:
area_scan_polynomial
与area_scan_division
是两种不同的面阵相机畸变校正模型,其核心差异可通过以下对比体现:
. 参数结构与畸变建模能力
属性 | division模型 | polynomial模型 |
---|---|---|
核心参数 | 单参数Kappa描述径向畸变 | 多参数组合(K1-K3径向+P1-P2切向畸变) |
畸变类型 | 仅径向畸变(桶形/枕形) | 径向畸变 + 切向畸变 + 透视投影畸变6 |
光学中心位置 | 固定在图像中心 | 可自定义中心位置 |
- 适用场景对比
- division模型优势
- 计算速度快(直接数学反演即可完成畸变校正)
- 标定稳定性高(尤其适用于少量标定图像或视野覆盖不全的情况)
- 典型应用:标准工业镜头(畸变率<5%)的场景
- polynomial模型优势
- 支持高阶非线性畸变校正(如广角镜头/鱼眼镜头的复杂畸变)
- 可实现亚像素级精度(适用于超高精度测量需求)
- 典型应用:医疗内窥镜、大视场角工业检测等场景
- Halcon实现差异
-
初始化参数示例
halconCopy Code* division模型参数示例 CameraParamDivision := ['area_scan_division', 0.012, 0, 0.00000375, 0.00000375, 640, 480, 1280, 960]* polynomial模型参数示例 CameraParamPoly := ['area_scan_polynomial', 0.016, 0.000005, 0.000005, 320, 240, 640, 480, 0.1, -0.05, 0.001]
- polynomial模型参数包含畸变多项式系数(如K1-K3、P1-P2)且支持自定义光学中心坐标
-
标定效率
指标 division模型 polynomial模型 标定数据需求 ≥5张标定图像8 ≥15张标定图像 计算耗时 约1-2秒(典型值) 约5-15秒(迭代计算)
- 选型建议
场景特征 | 推荐模型 |
---|---|
简单畸变、实时性要求高 | division模型 |
复杂畸变、需要亚像素级精度 | polynomial模型 |
广角镜头(视场角>60°) | polynomial模型 |
实际项目中建议先用division模型快速验证系统可行性,若精度不达标则切换至polynomial模型进行精细标定。
6.2,标定相机。
* 1.5,标定图像(包含各个水平角度,倾斜角度,不同位置但是需要全部在视野内的图像)
* Collect mark positions and estimated poses for all
* calibration images
for Index := 1 to NumCalibImages by 1read_image (Image, 'sheet_of_light/connection_rod_calib_' + Index$'.2')dev_display (Image)* 后续标定板处于不同位置所对应的位姿与该索引对应,该索引不必从0开始find_calib_object (Image, CalibDataID, 0, 0, Index, [], [])get_calib_data_observ_points (CalibDataID, 0, 0, Index, Row, Column, _Index, Pose)get_calib_data_observ_contours (Contours, CalibDataID, 'caltab', 0, 0, Index)dev_set_color ('green')dev_display (Contours)gen_cross_contour_xld (Cross, Row, Column, 6, 0.785398)dev_set_color ('yellow')dev_display (Cross)
endfor
*
* 1.6,执行标定
calibrate_cameras (CalibDataID, Errors)
disp_message (WindowHandle, 'The camera calibration has been performed successfully', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
注意点:
标定板的位置可以倾斜,旋转,但必须在相机视野内。
6.3,计算LightPanelPose(光平面的位姿)。
* 2.1,获取世界坐标系在相机坐标系的位姿Index := 19get_calib_data (CalibDataID, 'calib_obj_pose', [0,Index], 'pose', CalTabPose)* 相机的位姿即世界坐标系在相机坐标系的位姿set_origin_pose (CalTabPose, 0.0, 0.0, CalTabThickness, CameraPose)read_image (CalTabImage1, 'sheet_of_light/connection_rod_calib_' + Index$'.2')dev_display (CalTabImage1)get_calib_data (CalibDataID, 'camera', 0, 'params', CameraParameters)disp_3d_coord_system (WindowHandle, CameraParameters, CameraPose, .01)disp_message (WindowHandle, 'World coordinate system', 'window', 12, 12, 'black', 'true')disp_continue_message (WindowHandle, 'black', 'true')stop ()
* 2.2,获取临时坐标系在相机坐标系的位置(此时的标定板相对于Index:=19的标定板旋转了90度)
Index := 20
get_calib_data (CalibDataID, 'calib_obj_pose', [0,Index], 'pose', CalTabPose)
set_origin_pose (CalTabPose, 0.0, 0.0, CalTabThickness, TmpCameraPose)
read_image (CalTabImage2, 'sheet_of_light/connection_rod_calib_' + Index$'.2')
dev_display (CalTabImage2)
disp_3d_coord_system (WindowHandle, CameraParameters, TmpCameraPose, .01)
disp_message (WindowHandle, 'Temporary coordinate system', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
read_image (ProfileImage1, 'sheet_of_light/connection_rod_lightline_019.png')* 2.3,计算索引为19被测物体(位于世界坐标系)激光轮廓线点在z=0的世界坐标系中的坐标
compute_3d_coordinates_of_light_line (ProfileImage1, MinThreshold, CameraParameters, [], CameraPose, X19, Y19, Z19)
if (|X19| == 0 or |Y19| == 0 or |Z19| == 0)dev_display (ProfileImage1)disp_message (WindowHandle, 'The profile MUST be oriented horizontally\nfor successfull processing!\nThe program will exit.', 'window', 12, 12, 'black', 'true')return ()
endif
*
ProfileImage1
* 2.4,计算索引为20位于被测物体(位于临时坐标系)激光轮廓线点在z=0的世界坐标系中的坐标
* Compute the 3D coordinates of the light line points
* in the plane z=0 of the TCS
read_image (ProfileImage2, 'sheet_of_light/connection_rod_lightline_020.png')
compute_3d_coordinates_of_light_line (ProfileImage2, MinThreshold, CameraParameters, TmpCameraPose, CameraPose, X20, Y20, Z20)
if (|X20| == 0 or |Y20| == 0 or |Z20| == 0)disp_message (WindowHandle, '为了成功处理,轮廓需要是大致水平!\n程序将退出.', 'window', 12, 12, 'black', 'true')return ()
endif
ProfileImage2
本地算子:compute_3d_coordinates_of_light_line
* coordinates to the plane z=0 of the local coordinate system.
* 计算线结构光打在被测物体上的轮廓线点的世界坐标*警告:如果配置文件不是大致水平方向X, Y和Z返回空元组。
*
* Initialize the output controls
X := []
Y := []
Z := []
*
* Check LocalCameraPose in order to determine if a transform
* has to be applied after the projection from the camera to
* the z=0 plane
if (LocalCameraPose == [])DoTransform := 0LocalCameraPose := ReferenceCameraPose
elseif (LocalCameraPose == ReferenceCameraPose)DoTransform := 0*
elseDoTransform := 1
endif
*
* Compute the pose for the projection of the
* local coordinate system to the plane z=0
pose_to_hom_mat3d (LocalCameraPose, HomMat3D_LocalToCam)
*
* Compute the homography which transform the 3D-coordinates
* of points from the local coordinate system to the reference
* coordinate system
if (DoTransform)* 计算出临时坐标系在世界坐标系的位姿pose_to_hom_mat3d (ReferenceCameraPose, HomMat3D_ReferenceToCam)hom_mat3d_invert (HomMat3D_ReferenceToCam, HomMat3D_CamToReference)hom_mat3d_compose (HomMat3D_CamToReference, HomMat3D_LocalToCam, HomMat3D_LocalToReference)
endif
*
* Determine the profile region and test if the profile is
* oriented roughly horizontal
threshold (ProfileImage, Region, MinGray, 999999999)
orientation_region (Region, Phi)
if (cos(Phi) > cos(rad(135)) and cos(Phi) < cos(rad(45)))* The detected profile is NOT oriented roughly horizontal,* therefore return empty tuples X, Y and Z.return ()
endif
dilation_circle (Region, RegionDilation, 5.5)
smallest_rectangle1 (RegionDilation, Row1, Column1, Row2, Column2)
gen_rectangle1 (ProfileRegion, Row1, Column1, Row2, Column2)
get_domain (ProfileImage, Domain)
intersection (ProfileRegion, Domain, ProfileRegion)
*
* 获取光线上各点的坐标
create_sheet_of_light_model (ProfileRegion, ['min_gray','num_profiles'], [MinGray,1], SheetOfLightHandle)
* 从光片模型生成的图像中提取激光条纹的亚像素剖面数据
* 输出剖面点的坐标和灰度值,用于后面的三维重建和缺陷检测
measure_profile_sheet_of_light (ProfileImage, SheetOfLightHandle, [])* 未标定的视差图像(亚像素行坐标)
get_sheet_of_light_result (Disparity, SheetOfLightHandle, 'disparity')
clear_sheet_of_light_model (SheetOfLightHandle)
*
* Get the 3D-coordinates of the points on the light line
* in the coordinate system defined by ReferenceCameraPose
get_domain (Disparity, DisparityDomain)
get_region_points (DisparityDomain, Rows, Columns)
* Disparities:在这里并非表示常见的灰度值,而是表示激光条纹在亚像素级的行坐标偏移量
get_grayval (Disparity, Rows, Columns, Disparities)
*
* Project those points to the z=0 plane of the WCS
* 将激光条纹的坐标变化到世界坐标系
* Disparities已包含行方向的亚像素的偏移信息(如激光条纹中心的行坐标),因此行参数不需要补偿
* 列需要补偿的原因是:图像可能从ROI(感兴趣区域)的某一列开始采集,COumn1表示ROI起始列坐标
image_points_to_world_plane (CameraParameters, LocalCameraPose, Disparities, Columns + Column1, 1.0, X, Y)
tuple_gen_const (|Columns|, 0.0, Z)
*
* If necessary, transform the 3D-coordinates from the
* 本地坐标系到参考坐标系
* system
if (DoTransform)* 从临时坐标系切换到世界坐标系affine_trans_point_3d (HomMat3D_LocalToReference, X, Y, Z, X, Y, Z)* hom_mat3d_to_pose (HomMat3D_LocalToReference, Pose)* pose 的z轴角度为90 度对应了index:=19与index:=20标定板的角度差异
endif
return ()
释疑点:
在Halcon中,get_grayval(Disparity, Rows, Columns, Disparities)
获取的信息取决于输入图像Disparity
的数据类型和用途,具体可分为以下两种情况:
1. 常规灰度图像
当Disparity
为普通灰度图像时:
- 获取内容:指定坐标点(
Rows, Columns
)的像素亮度值(灰度值) - 数值范围:通常为0-255(8位图像)或更大范围(如16位图像)
- 典型应用:图像分析、阈值分割等传统视觉任务
2. 光片测量(Sheet-of-Light)视差图像
当Disparity
由光片测量技术生成时(如通过get_sheet_of_light_result
获取):
- 获取内容:激光条纹的亚像素级行坐标偏移量(非亮度值)
- 数值特性:浮点型数据,表示条纹中心相对于参考位置的精确位移
- 核心用途:用于三维重建,需配合
image_points_to_world_plane
等算子计算世界坐标17
3. 关键区别总结
特征 | 常规灰度图像 | 光片测量视差图像 | 来源 |
---|---|---|---|
数据本质 | 像素亮度 | 几何位置偏移量 | |
存储形式 | 整数(如uint8/uint16) | 浮点数(float) | |
典型操作 | 滤波、直方图均衡化 | 三维坐标投影 |
4. 使用建议
5. 确认图像类型
通过get_image_type
检查Disparity
的图像类型(如’real’表示浮点型视差数据)
6. 避免混淆
光片测量中的“视差”是几何量,与立体视觉中的视差概念不同
* 2.5,获取光平面的法向量坐标Nx,Ny,Nz,和重心点坐标
fit_3d_plane_xyz ([X19,X20], [Y19,Y20], [Z19,Z20], Ox, Oy, Oz, Nx, Ny, Nz, MeanResidual)
if (|Nx| == 0 or |Ny| == 0 or |Nz| == 0)disp_message (WindowHandle, '提供的3d点太少,无法拟合光平面,\nor 这些点(几乎)共线!\n程序将退出.', 'window', 12, 12, 'black', 'true')return ()
endif
if (MeanResidual > 5e-5)disp_message (WindowHandle, '这个光平面不能拟合!\n的平均误差距离\n高于拟合标准: (' + (MeanResidual * 1000)$'.3' + 'mm). \n请检查这些点的质量和正确性.\n程序将退出!', 'window', 12, 21, 'black', 'true')return ()endif
本地算子:fit_3d_plane_xyz
,获取拟合光平面所需要的法向量。
*
* WARNING: If the system of equations is under-determined
* (i.e. if it has too few input coordinates in X, Y, Z),
* it cannot be solved and the procedure returns empty tuples
* for X, Y, and Z
*
* Perform some initializations
Ox := []
Oy := []
Oz := []
Nx := []
Ny := []
Nz := []
MeanResidual := []
*
* Test the size of X, Y and Z, and return if necessary
Size := |X|
if (Size < 3 or Size != |Y| or Size != |Z|)return ()
endif
*
* 计算重心点坐标
tuple_mean (X, Ox)
tuple_mean (Y, Oy)
tuple_mean (Z, Oz)
*
*将方程组建立为矩阵M并计算
*其奇异值分解。奇异向量
*M的对应其最小奇异值有
*拟合平面的法向量坐标。
create_matrix (3, |X|, [X - Ox,Y - Oy,Z - Oz], MatrixID_Mt)
* 交换矩阵的行列
transpose_matrix (MatrixID_Mt, MatrixID_M)
* 计算一个矩阵的奇异值分解。
svd_matrix (MatrixID_M, 'reduced', 'right', MatrixID_U, MatrixID_S, MatrixID_V)
get_value_matrix (MatrixID_S, [0,1,2], [0,1,2], SingularvaluesOfM)
tuple_sort_index (SingularvaluesOfM, Indices)
*
* Test if more than one singular value of M is (nearly) equal
* to zero. This indicates that the provided 3d points are
* inappropriate to fit the plane (e.g. they are nearly
* collinear or reduce to a single point).
* 检验是否有多个M的奇异值(近似)相等
* 到零。这表明所提供的3d点是
* 不适合拟合成平面(例如它们几乎是)
* 共线或减少到一个点)。
if (SingularvaluesOfM[Indices[0]] < 1e-9 and SingularvaluesOfM[Indices[1]] < 1e-9)return ()
endif
*
* 得到拟合平面的法向量坐标
get_value_matrix (MatrixID_V, [0,1,2], [Indices[0],Indices[0],Indices[0]], N)
create_matrix (3, 1, N, MatrixID_N)
Nx := N[0]
Ny := N[1]
Nz := N[2]
*
* Compute the mean residual distance between the 3d points
* and the fitted plane, in order to guess the quality of
* the fitted plane:
* 计算三维点之间的平均差异距离
* 和拟合平面,以便猜测质量
* 拟合平面:
mult_matrix (MatrixID_M, MatrixID_N, 'AB', MatrixID_MN)
get_full_matrix (MatrixID_MN, Distances)
Distances := abs(Distances)
* 误差值
MeanResidual := sum(Distances) / Size
*
* Clear the matrices used in the procedure
clear_matrix ([MatrixID_MN,MatrixID_N,MatrixID_V,MatrixID_S,MatrixID_U,MatrixID_M,MatrixID_Mt])
return ()
* 2.6,根据重心点和法向量计算出光平面在世界坐标系的位姿
* 使得z=0定义的平面get_light_plane_pose (Ox, Oy, Oz, Nx, Ny, Nz, LightPlanePose)
if (|LightPlanePose| != 7)disp_message (WindowHandle, 'The pose of the light plane could not be\ndetermined. Please verify that the vector\npassed at input of the procedure\nget_light_plane_pose() is not null.\nThe program will exit!', 'window', -1, -2, 'black', 'true')return ()
endif* 2.7,显示光平面位姿信息
String := ['LightPlanePose: ',' Tx = ' + LightPlanePose[0]$'.3' + ' m',' Ty = ' + LightPlanePose[1]$'.3' + ' m',' Tz = ' + LightPlanePose[2]$'.3' + ' m',' alpha = ' + LightPlanePose[3]$'.4' + '°',' beta = ' + LightPlanePose[4]$'.4' + '°',' gamma = ' + LightPlanePose[5]$'.4' + '°',' type = ' + LightPlanePose[6]]
disp_message (WindowHandle, String, 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_clear_window ()
光平面位姿:
6.4,计算MovementPose。
* 3.1,计算标定板沿y axis 位移前后的位姿
read_image (CaltabImagePos1, 'sheet_of_light/caltab_at_position_1.png')
read_image (CaltabImagePos20, 'sheet_of_light/caltab_at_position_2.png')
StepNumber := 19* 标定板移动前(沿y axis 方向)
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_polynomial', CameraParameters)
* Compute the pose of the calibration table in each image
find_calib_object (CaltabImagePos1, CalibDataID, 0, 0, NumCalibImages + 1, [], [])* 从标定数据模型中提取未优化位姿
get_calib_data_observ_points (CalibDataID, 0, 0, NumCalibImages + 1, Row1, Column1, Index1, CameraPosePos1)
dev_display (CaltabImagePos1)
disp_3d_coord_system (WindowHandle,CameraParameters, CameraPosePos1, 0.01)
* 标定板移动后(位置2相对位置1在y axis 上进行移动的些许)
find_calib_object (CaltabImagePos20, CalibDataID, 0, 0, NumCalibImages + 2, [], [])
get_calib_data_observ_points (CalibDataID, 0, 0, NumCalibImages + 2, Row1, Column1, Index1, CameraPosePos20)
dev_display (CaltabImagePos20)
disp_3d_coord_system (WindowHandle,CameraParameters, CameraPosePos20, 0.01)
* Clear the model
clear_calib_data (CalibDataID)
位移前:
位移后:
* 3.2,将在相机坐标系中的位姿转换到世界坐标系
set_origin_pose (CameraPosePos1, 0.0, 0.0, CalTabThickness, CameraPosePos1)
set_origin_pose (CameraPosePos20, 0.0, 0.0, CalTabThickness, CameraPosePos20)
pose_to_hom_mat3d (CameraPosePos1, HomMat3DPos1ToCamera)
pose_to_hom_mat3d (CameraPosePos20, HomMat3DPos20ToCamera)
pose_to_hom_mat3d (CameraPose, HomMat3DWorldToCamera)
hom_mat3d_invert (HomMat3DWorldToCamera, HomMat3DCameraToWorld)
hom_mat3d_compose (HomMat3DCameraToWorld, HomMat3DPos1ToCamera, HomMat3DPos1ToWorld)
hom_mat3d_compose (HomMat3DCameraToWorld, HomMat3DPos20ToCamera, HomMat3DPos20ToWorld)* 3.3,计算观察点(0,0,0)在位移前后的偏移affine_trans_point_3d (HomMat3DPos1ToWorld, 0,0, 0, StartX, StartY, StartZ)affine_trans_point_3d (HomMat3DPos20ToWorld, 0, 0, 0, EndX, EndY, EndZ)
* 标定板移动的位姿
MovementPoseNSteps := [EndX - StartX,EndY - StartY,EndZ - StartZ,0,0,0,0]
* 计算每一步移动的位姿
MovementPose := MovementPoseNSteps / StepNumberString := ['标定板每一步移动的位姿\n(位置1到位置2共移动了19步)','MovementPose: ',' Tx = ' + MovementPose[0]$'.3' + ' m',' Ty = ' + MovementPose[1]$'.3' + ' m',' Tz = ' + MovementPose[2]$'.3' + ' m',' alpha = ' + MovementPose[3] + '°',' beta = ' + MovementPose[4] + '°',' gamma = ' + MovementPose[5] + '°',' type = ' + MovementPose[6]]
disp_message (WindowHandle, String, 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_clear_window ()
MovementPose信息:
释疑点:
MovementPose 的物理内涵与触发机制
- 参数定义与触发逻辑
- 核心意义
MovementPose
描述的是 物体在运动过程中相对于初始位姿的刚体变换(包含旋转矩阵 RR 和平移向量 TT),而非严格按时间间隔更新。其更新触发取决于外部运动系统的反馈机制(如编码器脉冲、机械臂位姿信号等)。 - 时间关联性
- 若运动系统以固定频率(如每秒)反馈位姿数据,则
MovementPose
可能每秒更新一次 - 但在 非匀速运动场景 中,位姿更新频率会动态调整,与时间间隔无直接绑定
- 若运动系统以固定频率(如每秒)反馈位姿数据,则
- 典型应用模式
-
同步触发模式
通过外部触发信号(如机械臂到达指定位置时发送脉冲)更新MovementPose
,确保扫描动作与位姿数据严格同步。 -
异步事件驱动
在自由运动场景(如传送带连续运行)中,根据运动系统实时推送的位姿流数据更新MovementPose
,此时更新频率由硬件通信速率决定。
- 技术对比
参数更新机制 | 触发条件 | 适用场景 |
---|---|---|
时间驱动 | 固定采样周期(如1秒) | 匀速运动且对实时性要求低的场景 |
事件驱动 | 运动状态变化(如位移超阈值) | 高精度动态扫描(工业检测、机器人抓取) |
实际工程中,通常采用事件驱动模式以保证点云数据与物体实际运动严格匹配。
6.5,激光三角应用。
* Part 4: 三角测量应用:生成3D点云。* Read an already acquired disparity map from file
read_image (Disparity, 'sheet_of_light/connection_rod_disparity.tif')
* Create a model and set the required parameters
gen_rectangle1 (ProfileRegion, 120, 75, 195, 710)
* min_gray:轮廓提取的最小灰度值;num_profiles:提取的轮廓数量;ambiguity_solving:解决深度歧义的方法,值"first":优先选择第一个有效解
create_sheet_of_light_model (ProfileRegion, ['min_gray','num_profiles','ambiguity_solving'], [70,290,'first'], SheetOfLightModelID)
set_sheet_of_light_param (SheetOfLightModelID, 'calibration', 'xyz')
set_sheet_of_light_param (SheetOfLightModelID, 'scale', 'mm')
set_sheet_of_light_param (SheetOfLightModelID, 'camera_parameter', CameraParameters)
set_sheet_of_light_param (SheetOfLightModelID, 'camera_pose', CameraPose)
* 光平面在世界坐标系的位姿
set_sheet_of_light_param (SheetOfLightModelID, 'lightplane_pose', LightPlanePose)
* 实时更新位姿,确保点云数据对齐世界坐标系
* 即每移动一步的位姿
* 物体在运动过程中相对于初始位姿的刚体变换set_sheet_of_light_param (SheetOfLightModelID, 'movement_pose', MovementPose)
*
* Apply the calibration transforms and
* get the resulting calibrated coordinates
apply_sheet_of_light_calibration (Disparity, SheetOfLightModelID)
get_sheet_of_light_result (X, SheetOfLightModelID, 'x')
get_sheet_of_light_result (Y, SheetOfLightModelID, 'y')
get_sheet_of_light_result (Z, SheetOfLightModelID, 'z')
clear_sheet_of_light_model (SheetOfLightModelID)
*
* 4.1,显示生成的 Z-coordinates图像
*dev_close_window ()
get_image_size (Disparity, Width, Height)
dev_open_window (Height + 10, 0, Width * .5, Height * .5, 'black', WindowHandle3)
set_display_font (WindowHandle3, 14, 'mono', 'true', 'false')
dev_set_lut ('temperature')
dev_display (Z)
disp_message (WindowHandle3, 'Calibrated Z-coordinates', 'window', 12, 12, 'black', 'true')
*
* 4.2,显示生成的 Y-coordinates图像
dev_open_window ((Height + 10), Width * .5, Width * .5, Height * .5, 'black', WindowHandle2)
set_display_font (WindowHandle2, 14, 'mono', 'true', 'false')
dev_display (Y)
disp_message (WindowHandle2, 'Calibrated Y-coordinates', 'window', 12, 12, 'black', 'true')
*
* 4.3,显示生成的 Z-coordinates图像
dev_open_window (Height*1.5, 0, Width * .5, Height * .5, 'black', WindowHandle1)
dev_display (X)
set_display_font (WindowHandle1, 14, 'mono', 'true', 'false')
disp_message (WindowHandle1, 'Calibrated X-coordinates', 'window', 12, 12, 'black', 'true')
* 合成 3D 点云模型,点云数据默认以世界坐标系为基础进行渲染
xyz_to_object_model_3d (X, Y, Z, ObjectModel3D)*-----------
* part5: 显示光平面与3D对象模型
* 光平面,这里的光平面位姿是指在世界坐标系中的位姿
gen_plane_object_model_3d (LightPlanePose, [-80,-80,80,80], [80,-80,-80,80], ObjectModel3D1)
dev_set_color ('red')
visualize_object_model_3d (WindowHandle,[ObjectModel3D,ObjectModel3D1],\[], [], ['lut','intensity','color_1','alpha_1','disp_pose'], ['color1','coord_z','red',0.6,'true'], [], [], [], PoseOut)
释疑点:
visualize_object_model_3d 算子的默认显示坐标系:
- 核心坐标系关系
- 点云数据默认以**世界坐标系(场景坐标系 SCS)**为基础进行渲染。此时虚拟相机的观察视角会通过
CamParam
和PoseIn
参数调整,但点云本身的坐标仍与世界坐标系对齐。
- 点云数据默认以**世界坐标系(场景坐标系 SCS)**为基础进行渲染。此时虚拟相机的观察视角会通过
- 默认姿态与相机参数的影响
- 当
PoseIn
参数为空时,算子会自动计算初始位姿,此时点云的显示位置仍基于世界坐标系,但会通过虚拟相机的视角参数(如CamParam
)进行投影变换。 - 若未显式设置相机内参(
CamParam
为空),算子会根据窗口尺寸生成默认参数,但坐标系基准不变。
- 当
- 用户交互对坐标系的修正
- 通过鼠标操作(旋转、平移)调整点云姿态后,输出的
PoseOut
参数表示点云在世界坐标系下的新位姿,而非相机坐标系下的相对位置。
- 通过鼠标操作(旋转、平移)调整点云姿态后,输出的
总结:该算子默认以世界坐标系为基准渲染点云,虚拟相机仅提供观察视角的变换,不会改变点云本身在世界坐标系中的实际坐标。
示意图
点云效果及光平面显示
红色为Light plane
,红轴为 x axis,绿轴为 y axis,蓝轴为 z axis。
7,完整代码。
* 案例库:cablibrate_sheet_of_light_calplate.hdev* 目的:
* 计算激光三角测量所需的LightPlanePose(光平面位姿)与MovementPose(每步移动位姿),完成激光三角测量* 描述:
* 通过计算结构光在亮处不同位姿的被测物上形成轮廓线的点坐标(世界坐标系)拟合出LightPlanePose
* 通过在y axis移动n步的标定板位姿变化计算出MovementPose(每步移动位姿)
dev_update_off ()
dev_close_window ()
read_image (ProfileImage, 'sheet_of_light/connection_rod_001.png')
get_image_size (ProfileImage, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
dev_set_draw ('margin')
dev_set_line_width (3)
dev_set_color ('lime green')
dev_set_lut ('default')
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
*
* -------
* Part 1: 执行相机的标定
* -------
*
* 1.1,默认的相机内参
StartParameters := [0.0125,0.0,0.0,0.0,0.0,0.0,0.000006,0.000006,376.0,120.0,752,240]* 1.2,标定板描述文件
* caltab_30mm.descr':Distance between mark centers [meter]: 0.00375
CalTabDescription := 'caltab_30mm.descr'* 1.3,标定板的厚度(单位:m)
CalTabThickness := .00063* 1.4,标定板图像数量
NumCalibImages := 20
*
* Initialize a calibration data model
create_calib_data ('calibration_object', 1, 1, CalibDataID)* 1.4,设置标定参数
* 'area_scan_polynomial':复杂畸变、需要亚像素级精度。至少需要15张标定图像。
* 'division':至少需要5张标定图像。
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_polynomial', StartParameters)
set_calib_data_calib_object (CalibDataID, 0, CalTabDescription)* 1.5,标定图像(包含各个水平角度,倾斜角度,不同位置但是需要全部在视野内的图像)
* Collect mark positions and estimated poses for all
* calibration images
for Index := 1 to NumCalibImages by 1read_image (Image, 'sheet_of_light/connection_rod_calib_' + Index$'.2')dev_display (Image)* 后续标定板处于不同位置所对应的位姿与该索引对应,该索引不必从0开始find_calib_object (Image, CalibDataID, 0, 0, Index, [], [])get_calib_data_observ_points (CalibDataID, 0, 0, Index, Row, Column, _Index, Pose)get_calib_data_observ_contours (Contours, CalibDataID, 'caltab', 0, 0, Index)dev_set_color ('green')dev_display (Contours)gen_cross_contour_xld (Cross, Row, Column, 6, 0.785398)dev_set_color ('yellow')dev_display (Cross)
endfor
*
* 1.6,执行标定
calibrate_cameras (CalibDataID, Errors)
disp_message (WindowHandle, 'The camera calibration has been performed successfully', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
*
* -------
* Part 2: 计算光平面相对于世界坐标系的位姿
* -------
dev_set_colored (3)
MinThreshold := 80* 2.1,获取世界坐标系在相机坐标系的位姿
Index := 19
get_calib_data (CalibDataID, 'calib_obj_pose', [0,Index], 'pose', CalTabPose)
set_origin_pose (CalTabPose, 0.0, 0.0, CalTabThickness, CameraPose)
read_image (CalTabImage1, 'sheet_of_light/connection_rod_calib_' + Index$'.2')
dev_display (CalTabImage1)
get_calib_data (CalibDataID, 'camera', 0, 'params', CameraParameters)
disp_3d_coord_system (WindowHandle, CameraParameters, CameraPose, .01)
disp_message (WindowHandle, 'World coordinate system', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
*
* Definition of a temporary coordinate system (TCS):
* The TCS is also defined implicitly by choosing another
* calibration image. Here again we shift the origin of
* the coordinate system in order to take the thickness
* of the calibration table into account.
* 2.2,获取临时坐标系在相机坐标系的位置(此时的标定板相对于Index:=19的标定板旋转了90度)
Index := 20
get_calib_data (CalibDataID, 'calib_obj_pose', [0,Index], 'pose', CalTabPose)
set_origin_pose (CalTabPose, 0.0, 0.0, CalTabThickness, TmpCameraPose)
read_image (CalTabImage2, 'sheet_of_light/connection_rod_calib_' + Index$'.2')
dev_display (CalTabImage2)
disp_3d_coord_system (WindowHandle, CameraParameters, TmpCameraPose, .01)
disp_message (WindowHandle, 'Temporary coordinate system', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()*
* dev_clear_window ()
read_image (ProfileImage1, 'sheet_of_light/connection_rod_lightline_019.png')* 2.3,计算索引为19被测物体(位于世界坐标系)激光轮廓线点在z=0的世界坐标系中的坐标
compute_3d_coordinates_of_light_line (ProfileImage1, MinThreshold, CameraParameters, [], CameraPose, X19, Y19, Z19)
if (|X19| == 0 or |Y19| == 0 or |Z19| == 0)dev_display (ProfileImage1)disp_message (WindowHandle, 'The profile MUST be oriented horizontally\nfor successfull processing!\nThe program will exit.', 'window', 12, 12, 'black', 'true')return ()
endif
*
* 2.4,计算索引为20位于被测物体(位于临时坐标系)激光轮廓线点在z=0的世界坐标系中的坐标
* Compute the 3D coordinates of the light line points
* in the plane z=0 of the TCS
read_image (ProfileImage2, 'sheet_of_light/connection_rod_lightline_020.png')
compute_3d_coordinates_of_light_line (ProfileImage2, MinThreshold, CameraParameters, TmpCameraPose, CameraPose, X20, Y20, Z20)
if (|X20| == 0 or |Y20| == 0 or |Z20| == 0)disp_message (WindowHandle, '为了成功处理,轮廓需要是大致水平!\n程序将退出.', 'window', 12, 12, 'black', 'true')return ()
endif
* * 2.5,获取光平面的法向量坐标Nx,Ny,Nz,和重心点坐标
fit_3d_plane_xyz ([X19,X20], [Y19,Y20], [Z19,Z20], Ox, Oy, Oz, Nx, Ny, Nz, MeanResidual)
if (|Nx| == 0 or |Ny| == 0 or |Nz| == 0)disp_message (WindowHandle, '提供的3d点太少,无法拟合光平面,\nor 这些点(几乎)共线!\n程序将退出.', 'window', 12, 12, 'black', 'true')return ()
endif
if (MeanResidual > 5e-5)disp_message (WindowHandle, '这个光平面不能拟合!\n的平均误差距离\n高于拟合标准: (' + (MeanResidual * 1000)$'.3' + 'mm). \n请检查这些点的质量和正确性.\n程序将退出!', 'window', 12, 21, 'black', 'true')return ()endif
*
* 2.6,根据重心点和法向量计算出光平面在世界坐标系的位姿
* 使得z=0定义的平面get_light_plane_pose (Ox, Oy, Oz, Nx, Ny, Nz, LightPlanePose)
if (|LightPlanePose| != 7)disp_message (WindowHandle, 'The pose of the light plane could not be\ndetermined. Please verify that the vector\npassed at input of the procedure\nget_light_plane_pose() is not null.\nThe program will exit!', 'window', -1, -2, 'black', 'true')return ()
endif* 2.7,显示光平面位姿信息
String := ['LightPlanePose: ',' Tx = ' + LightPlanePose[0]$'.3' + ' m',' Ty = ' + LightPlanePose[1]$'.3' + ' m',' Tz = ' + LightPlanePose[2]$'.3' + ' m',' alpha = ' + LightPlanePose[3]$'.4' + '°',' beta = ' + LightPlanePose[4]$'.4' + '°',' gamma = ' + LightPlanePose[5]$'.4' + '°',' type = ' + LightPlanePose[6]]
disp_message (WindowHandle, String, 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* dev_clear_window ()
*
* -------
* Part 3:获取MovementPose(每移动一步的姿态)* 3.1,计算标定板沿y axis 位移前后的位姿
read_image (CaltabImagePos1, 'sheet_of_light/caltab_at_position_1.png')
read_image (CaltabImagePos20, 'sheet_of_light/caltab_at_position_2.png')
StepNumber := 19* 标定板移动前(沿y axis 方向)
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_polynomial', CameraParameters)
* Compute the pose of the calibration table in each image
find_calib_object (CaltabImagePos1, CalibDataID, 0, 0, NumCalibImages + 1, [], [])* 从标定数据模型中提取未优化位姿
get_calib_data_observ_points (CalibDataID, 0, 0, NumCalibImages + 1, Row1, Column1, Index1, CameraPosePos1)
dev_display (CaltabImagePos1)
disp_3d_coord_system (WindowHandle,CameraParameters, CameraPosePos1, 0.01)
* 标定板移动后(位置2相对位置1在y axis 上进行移动的些许)
find_calib_object (CaltabImagePos20, CalibDataID, 0, 0, NumCalibImages + 2, [], [])
get_calib_data_observ_points (CalibDataID, 0, 0, NumCalibImages + 2, Row1, Column1, Index1, CameraPosePos20)
dev_display (CaltabImagePos20)
disp_3d_coord_system (WindowHandle,CameraParameters, CameraPosePos20, 0.01)
* Clear the model
clear_calib_data (CalibDataID)
*
* 3.2,将在相机坐标系中的位姿转换到世界坐标系
set_origin_pose (CameraPosePos1, 0.0, 0.0, CalTabThickness, CameraPosePos1)
set_origin_pose (CameraPosePos20, 0.0, 0.0, CalTabThickness, CameraPosePos20)
pose_to_hom_mat3d (CameraPosePos1, HomMat3DPos1ToCamera)
pose_to_hom_mat3d (CameraPosePos20, HomMat3DPos20ToCamera)
pose_to_hom_mat3d (CameraPose, HomMat3DWorldToCamera)
hom_mat3d_invert (HomMat3DWorldToCamera, HomMat3DCameraToWorld)
hom_mat3d_compose (HomMat3DCameraToWorld, HomMat3DPos1ToCamera, HomMat3DPos1ToWorld)
hom_mat3d_compose (HomMat3DCameraToWorld, HomMat3DPos20ToCamera, HomMat3DPos20ToWorld)* 3.3,计算观察点(0,0,0)在位移前后的偏移affine_trans_point_3d (HomMat3DPos1ToWorld, 0,0, 0, StartX, StartY, StartZ)affine_trans_point_3d (HomMat3DPos20ToWorld, 0, 0, 0, EndX, EndY, EndZ)
* 标定板移动的位姿
MovementPoseNSteps := [EndX - StartX,EndY - StartY,EndZ - StartZ,0,0,0,0]
* 计算每一步移动的位姿
MovementPose := MovementPoseNSteps / StepNumberString := ['标定板每一步移动的位姿\n(位置1到位置2共移动了19步)','MovementPose: ',' Tx = ' + MovementPose[0]$'.3' + ' m',' Ty = ' + MovementPose[1]$'.3' + ' m',' Tz = ' + MovementPose[2]$'.3' + ' m',' alpha = ' + MovementPose[3] + '°',' beta = ' + MovementPose[4] + '°',' gamma = ' + MovementPose[5] + '°',' type = ' + MovementPose[6]]
disp_message (WindowHandle, String, 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
dev_clear_window ()
*
* -------
* Part 4: 三角测量应用:生成3D点云。
* -------
*
* Read an already acquired disparity map from file
read_image (Disparity, 'sheet_of_light/connection_rod_disparity.tif')
* Create a model and set the required parameters
gen_rectangle1 (ProfileRegion, 120, 75, 195, 710)
* min_gray:轮廓提取的最小灰度值;num_profiles:提取的轮廓数量;ambiguity_solving:解决深度歧义的方法,值"first":优先选择第一个有效解
create_sheet_of_light_model (ProfileRegion, ['min_gray','num_profiles','ambiguity_solving'], [70,290,'first'], SheetOfLightModelID)
set_sheet_of_light_param (SheetOfLightModelID, 'calibration', 'xyz')
set_sheet_of_light_param (SheetOfLightModelID, 'scale', 'mm')
set_sheet_of_light_param (SheetOfLightModelID, 'camera_parameter', CameraParameters)
set_sheet_of_light_param (SheetOfLightModelID, 'camera_pose', CameraPose)
* 光平面在世界坐标系的位姿
set_sheet_of_light_param (SheetOfLightModelID, 'lightplane_pose', LightPlanePose)
* 实时更新位姿,确保点云数据对齐世界坐标系
* 即每移动一步的位姿
* 物体在运动过程中相对于初始位姿的刚体变换set_sheet_of_light_param (SheetOfLightModelID, 'movement_pose', MovementPose)
*
* Apply the calibration transforms and
* get the resulting calibrated coordinates
apply_sheet_of_light_calibration (Disparity, SheetOfLightModelID)
get_sheet_of_light_result (X, SheetOfLightModelID, 'x')
get_sheet_of_light_result (Y, SheetOfLightModelID, 'y')
get_sheet_of_light_result (Z, SheetOfLightModelID, 'z')
clear_sheet_of_light_model (SheetOfLightModelID)
*
* 4.1,显示生成的 Z-coordinates图像
*dev_close_window ()
get_image_size (Disparity, Width, Height)
dev_open_window (Height + 10, 0, Width * .5, Height * .5, 'black', WindowHandle3)
set_display_font (WindowHandle3, 14, 'mono', 'true', 'false')
dev_set_lut ('temperature')
dev_display (Z)
disp_message (WindowHandle3, 'Calibrated Z-coordinates', 'window', 12, 12, 'black', 'true')
*
* 4.2,显示生成的 Y-coordinates图像
dev_open_window ((Height + 10), Width * .5, Width * .5, Height * .5, 'black', WindowHandle2)
set_display_font (WindowHandle2, 14, 'mono', 'true', 'false')
dev_display (Y)
disp_message (WindowHandle2, 'Calibrated Y-coordinates', 'window', 12, 12, 'black', 'true')
*
* 4.3,显示生成的 Z-coordinates图像
dev_open_window (Height*1.5, 0, Width * .5, Height * .5, 'black', WindowHandle1)
dev_display (X)
set_display_font (WindowHandle1, 14, 'mono', 'true', 'false')
disp_message (WindowHandle1, 'Calibrated X-coordinates', 'window', 12, 12, 'black', 'true')
* 合成 3D 点云模型,点云数据默认以世界坐标系为基础进行渲染
xyz_to_object_model_3d (X, Y, Z, ObjectModel3D)*-----------
* part5: 显示光平面与3D对象模型
* 光平面,这里的光平面位姿是指在世界坐标系中的位姿
gen_plane_object_model_3d (LightPlanePose, [-80,-80,80,80], [80,-80,-80,80], ObjectModel3D1)
dev_set_color ('red')
visualize_object_model_3d (WindowHandle,[ObjectModel3D,ObjectModel3D1],\[], [], ['lut','intensity','color_1','alpha_1','disp_pose'], ['color1','coord_z','red',0.6,'true'], [], [], [], PoseOut)