激光三角测量标定与应用

文章目录

      • 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θxsin(θ1θ2)axsinθ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)xsin(θ1+θ2+θ3)axsinθ3cosθ1
式中, θ 1 \theta_1 θ1为激光束光轴与被测面的法线夹角; θ 2 \theta_2 θ2为成像透镜与被测面的法线夹角; θ 3 \theta_3 θ3为探测器光轴与成像透镜光轴之间的夹角。

3.3,两种三角位移传感器特性的比较

基于三角测量法的传感器称为激光三角位移传感器,具体可以分为直射式激光三角位移传感器和斜射式激光三角位移传感器。这两种传感器都可以对被测面进行高精度、高速度的非接触式测量,但比较起来有以下几点区别:

  1. 斜射式可以接收来自被测物体的正反射光,比较适合测量表面接近镜面的物体。直射式由于其接收散射光的特点,适合于测量散射性能好的表面。
  2. 直射式光斑较小,光强集中,不会因被测面不垂直而扩大光斑,而且一般体积比较小。斜射式传感器分辨率高于直射式,但他的测量范围较小,体积较大。

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相机模型,原因如下:

  1. 高精度需求‌:多项式模型使用K1-K3三个参数描述径向畸变,P1-P2两个参数描述切向畸变,比division单参数模型精度更高
  2. 畸变校正‌:激光三角测量对镜头畸变敏感,polynomial模型能更好校正广角镜头的桶形/枕形畸变
  3. 系统架构‌:标定过程需要相机内外参和激光平面参数,多项式模型提供更完整的参数空间
2,measure_profile_sheet_of_light

光片测量技术(Sheet-of-Light)中的关键操作,主要用于激光条纹剖面数据的精确提取与分析。其核心功能与特性如下:

一、功能定位

  1. 核心作用

    • 从光片模型生成的图像中提取激光条纹的亚像素级剖面数据。
    • 输出剖面点的坐标和灰度值,用于后续三维重建或缺陷检测。
  2. 技术关联

    • 需配合create_sheet_of_light_model创建的光片模型句柄使用。

二、参数解析

参数类型关键参数作用说明
输入参数SheetOfLightHandle已创建的光片模型句柄2
ProfileImage包含激光条纹的输入图像
输出参数Profile提取的剖面点坐标序列
GrayValues对应点的灰度值数组

三、典型应用场景

  1. 工业三维测量

    • 金属零件表面轮廓重建(如连接杆的深度测量)
    • 注塑模具孔洞检测(通过剖面灰度突变分析)
  2. 流程示例

    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)
    

四、性能优化建议

  1. 参数调优
    • 通过set_sheet_of_light_param调整'profile_width'(推荐3-10像素)提升条纹定位精度
    • 设置'min_gray'过滤环境光干扰
  2. 硬件协同
    • 使用高动态范围相机避免激光过曝
    • 标定阶段需保证激光平面与相机视角夹角>30°

该算子是实现激光三角测量三维重建的核心环节,其精度直接影响最终测量结果。

3,get_sheet_of_light_result

是光片测量技术(Sheet-of-Light)的核心数据提取接口,用于获取线结构光三维测量的标定或未标定结果。其功能特性与使用方法如下:

一、核心功能

  1. 数据输出类型

    • 支持5种结果类型通过
      参数指定:
      • 'disparity':未标定的视差图像(亚像素行坐标)
      • 'score':条纹匹配质量评分(依赖'score_type'参数)
      • 'x'/'y'/'z':标定后的三维坐标点云

二、参数详解

参数类别参数名作用说明
输入参数SheetOfLightModelID已初始化的光片模型句柄
ResultName指定输出数据类型(默认'disparity'
输出参数ResultValue根据ResultName返回对应数据

三、典型应用场景

  1. 工业三维检测
    • 使用'z'结果进行零件高度公差验证
    • 通过'score'分析激光条纹质量,识别表面缺陷
  2. 代码示例
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_polynomialarea_scan_division是两种不同的面阵相机畸变校正模型,其核心差异可通过以下对比体现:

. 参数结构与畸变建模能力

属性division模型polynomial模型
核心参数单参数Kappa描述径向畸变多参数组合(K1-K3径向+P1-P2切向畸变)
畸变类型仅径向畸变(桶形/枕形)径向畸变 + 切向畸变 + 透视投影畸变6
光学中心位置固定在图像中心可自定义中心位置

  • 适用场景对比
  1. division模型优势
    • 计算速度快(直接数学反演即可完成畸变校正)
    • 标定稳定性高(尤其适用于少量标定图像或视野覆盖不全的情况)
    • 典型应用:标准工业镜头(畸变率<5%)的场景
  2. polynomial模型优势
    • 支持高阶非线性畸变校正(如广角镜头/鱼眼镜头的复杂畸变)
    • 可实现亚像素级精度(适用于超高精度测量需求)
    • 典型应用:医疗内窥镜、大视场角工业检测等场景

  • Halcon实现差异
  1. 初始化参数示例

    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)且支持自定义光学中心坐标
  2. 标定效率

    指标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 的物理内涵与触发机制


  • 参数定义与触发逻辑
  1. 核心意义
    MovementPose 描述的是 ‌物体在运动过程中相对于初始位姿的刚体变换‌(包含旋转矩阵 RR 和平移向量 TT),而非严格按时间间隔更新。其更新触发取决于外部运动系统的反馈机制(如编码器脉冲、机械臂位姿信号等)。
  2. 时间关联性
    • 若运动系统以固定频率(如每秒)反馈位姿数据,则 MovementPose 可能每秒更新一次
    • 但在 ‌非匀速运动场景‌ 中,位姿更新频率会动态调整,与时间间隔无直接绑定

  • 典型应用模式
  1. 同步触发模式
    通过外部触发信号(如机械臂到达指定位置时发送脉冲)更新 MovementPose,确保扫描动作与位姿数据严格同步。

  2. 异步事件驱动
    在自由运动场景(如传送带连续运行)中,根据运动系统实时推送的位姿流数据更新 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 算子的默认显示坐标系:

  1. 核心坐标系关系
    • 点云数据默认以‌**世界坐标系(场景坐标系 SCS)**‌为基础进行渲染。此时虚拟相机的观察视角会通过 CamParamPoseIn 参数调整,但点云本身的坐标仍与世界坐标系对齐。
  2. 默认姿态与相机参数的影响
    • PoseIn 参数为空时,算子会自动计算初始位姿,此时点云的显示位置仍基于世界坐标系,但会通过虚拟相机的视角参数(如 CamParam)进行投影变换。
    • 若未显式设置相机内参(CamParam 为空),算子会根据窗口尺寸生成默认参数,但坐标系基准不变。
  3. 用户交互对坐标系的修正
    • 通过鼠标操作(旋转、平移)调整点云姿态后,输出的 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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83892.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高可用消息队列实战:AWS SQS 在分布式系统中的核心解决方案

引言&#xff1a;消息队列的“不可替代性” 在微服务架构和分布式系统盛行的今天&#xff0c;消息队列&#xff08;Message Queue&#xff09; 已成为解决系统解耦、流量削峰、异步处理等难题的核心组件。然而&#xff0c;传统的自建消息队列&#xff08;如RabbitMQ、Kafka&am…

人工智能核心知识:AI Agent 的四种关键设计模式

人工智能核心知识&#xff1a;AI Agent 的四种关键设计模式 一、引言 在人工智能领域&#xff0c;AI Agent&#xff08;人工智能代理&#xff09;是实现智能行为和决策的核心实体。它能够感知环境、做出决策并采取行动以完成特定任务。为了设计高效、灵活且适应性强的 AI Age…

平替BioLegend品牌-Elabscience PE Anti-Mouse Foxp3抗体:流式细胞术中的高效工具,助力免疫细胞分析!”

概述 调节性T细胞&#xff08;Treg&#xff09;在维持免疫耐受和抑制过度免疫反应中发挥关键作用&#xff0c;其标志性转录因子Foxp3&#xff08;Forkhead box P3&#xff09;是Treg功能研究的重要靶点。Elabscience 推出的抗小鼠Foxp3抗体&#xff08;3G3-E&#xff09;&…

编程日志5.13

邻接表的基础代码 #include<iostream> using namespace std; //邻接表的类声明 class Graph {private: //结构体EdgeNode表示图中的边结点,包含顶点vertex、权重weight和指向下一个边结点的指针next struct EdgeNode { int vertex; int weight; …

PowerBI 矩阵实现动态行内容(如前后销售数据)统计数据,以及过滤同时为0的数据

我们有一张活动表 和 一张销售表 我们想实现如下的效果&#xff0c;当选择某个活动时&#xff0c;显示活动前后3天的销售对比图&#xff0c;如下&#xff1a; 实现方法&#xff1a; 1.新建一个表&#xff0c;用于显示列&#xff1a; 2.新建一个度量值&#xff0c;用SELECTEDVA…

Prompt Tuning:高效微调大模型的新利器

Prompt Tuning(提示调优)是什么 Prompt Tuning(提示调优) 是大模型参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的重要技术之一,其核心思想是通过优化 连续的提示向量(而非整个模型参数)来适配特定任务。以下是关于 Prompt Tuning 的详细解析: 一、核心概念…

杰发科技AC7840——如何把结构体数据写到Dflash中

1. 结构体数据被存放在Pflash中 正常情况下&#xff0c;可以看到全局变量的结构体数据被存放在Pflash中 数字部分存在RAM中 2. 最小编程单位 8字节编程&#xff0c;因此如果结构体存放在Dfalsh中&#xff0c;进行写操作&#xff0c;需要写8字节的倍数 第一种办法&#xff1a;…

CSS 选择器入门

一、CSS 选择器基础&#xff1a;快速掌握核心概念 什么是选择器&#xff1f; CSS 选择器就像 “网页元素的遥控器”&#xff0c;用于定位 HTML 中的特定元素并应用样式。 /* 结构&#xff1a;选择器 { 属性: 值; } */ p { color: red; } /* 选择所有<p>元素&#xff0c;…

Anaconda3安装教程(附加安装包)Anaconda详细安装教程Anaconda3 最新版安装教程

多环境隔离 可同时维护生产环境、开发环境、测试环境&#xff0c;例如&#xff1a; conda create -n ml python3.10 # 创建机器学习环境 conda activate ml # 激活环境三、Anaconda3 安装教程 解压Anaconda3安装包 找到下载的 Anaconda3 安装包&#xff08;.ex…

现代计算机图形学Games101入门笔记(十七)

双向路径追踪 外观建模 散射介质 人的头发不能用在动画的毛发上。 动物的髓质Medulla特别大 双层圆柱模型应用 BSSRDF是BRDF的延伸。 天鹅绒用BRDF不合理&#xff0c;转成散射介质。 法线分布 光追很难处理微表面模型 光在微型细节上&#xff0c;光是一个波&#xff0c;会发生衍…

chrome源码中WeakPtr 跨线程使用详解:原理、风险与最佳实践

base::WeakPtr 在 Chromium 中 不能安全地跨线程使用。这是一个很关键的点&#xff0c;下面详细解释原因及正确用法。 &#x1f50d;原理与使用 ✅ 先说答案&#xff1a; base::WeakPtr 本质上是**线程绑定&#xff08;thread-affine&#xff09;**的。不能在多个线程之间创建…

hysAnalyser 从MPEG-TS导出ES功能说明

摘要 hysAnalyser 是一款特色的 MPEG-TS 数据分析工具。本文主要介绍了 hysAnalyser 从MPEG-TS 中导出选定的 ES 或 PES 功能(版本v1.0.003)&#xff0c;以便用户知悉和掌握这些功能&#xff0c;帮助分析和解决各种遇到ES或PES相关的实际问题。hysAnalyser 支持主流的MP1/MP2/…

C++(21):fstream的读取和写入

目录 1 ios::out 2 ios::in和is_open 3 put()方法 4 get()方法 4.1 读取单个字符 4.2 读取多个字符 4.3 设置终结符 5 getline() 1 ios::out 打开文件用于写入数据。如果文件不存在&#xff0c;则新建该文件&#xff1b;如果文件原来就存在&#xff0c;则打开时清除…

系统架构设计(十七):微服务数据一致性和高可用策略

数据一致性问题 问题本质 由于每个微服务拥有独立数据库&#xff0c;跨服务操作不能用传统的数据库事务&#xff0c;面临“分布式事务”一致性挑战。 数据一致性策略 策略核心思想应用场景优缺点强一致性&#xff08;Strong Consistency&#xff09;所有操作实时同步成功&a…

os agent智能体软件 - 第三弹 - 纯语音交互

前两期期我们发布了产品的初级形态&#xff0c;那时候还只能是“软件开发者”在本地配置使用&#xff0c;或者运行起来有个大黑框&#xff0c;使用起来美观度太差。 到今天大概20天&#xff0c;我们的第3版已经出来了&#xff0c;不仅做成了电脑端的exe软件&#xff08;任何人…

链表原理与实现:从单链表到LinkedList

1.链表的概念及结构 链表是一种物理存储结构上非连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。 可以形象的理解&#xff0c;在逻辑上来看&#xff0c;链表就像是一节节火车车厢。 链表的分类&#xff1a;链表的结构有很多种&#xff0c;单向…

替换word中的excel

PostMapping("/make/report/target/performance/first") public AjaxResult makeTargetReportFirst(RequestBody MakeReportDTO makeReportDTO) {Map<String, String> textReplaceMap new HashMap<>();// 替换日期LocalDateTime nowData LocalDateTime…

深入探索百度智能云千帆AppBuilder:从零开始构建AI应用

在数字化转型的浪潮中&#xff0c;企业对高效、智能的应用开发平台的需求日益增长。百度智能云千帆AppBuilder&#xff08;以下简称AppBuilder&#xff09;凭借其强大的功能和灵活的开发方式&#xff0c;成为企业级大模型应用开发的理想选择。本文将详细介绍如何使用AppBuilder…

测试工程师要如何开展单元测试

单元测试是软件开发过程中至关重要的环节&#xff0c;它通过验证代码的最小可测试单元(如函数、方法或类)是否按预期工作&#xff0c;帮助开发团队在早期发现和修复缺陷&#xff0c;提升代码质量和可维护性。以下是测试工程师开展单元测试的详细步骤和方法&#xff1a; 一、理…

NODE-I916 I721模块化电脑发布,AI算力与超低功耗的完美平衡

在智能工业与边缘计算蓬勃发展的今天&#xff0c;企业对计算设备的性能与能效需求日益严苛。全新推出NODE-I916与NODE-I721模块化电脑&#xff0c;分别搭载英特尔 酷睿™ Ultra 平台与Alder Lake-N平台&#xff0c;以差异化CPU配置为核心&#xff0c;为AI推理、工业自动化及嵌入…