原文作者:aircraft
原文鏈接:https://www.cnblogs.com/DOMLX/p/11555100.html
前言:最近公司項目用到halcon的3d模板匹配,三維重建,相機標定,所以最近都在研究這些,現在分享一下對激光三角測量示例的個人理解。
完整的激光三角測量視頻教程:https://www.bilibili.com/video/BV1cJ411M7DE
demo是calibrate_sheet_of_light_calplate.hdev,其中包括了相機標定過程已經激光標定過程
1.Reconstruct_Connection_Rod_Calib.hdev
先看一下這個halcon示例做了些什么:
通過一道激光照射過一個零件,留下了一個個片截圖,后面用於測量其深度信息。
而示例就是用光片模型的重建,對所有的connection_rod系列圖片進行處理,重建出原模型的圖像:
也可以看片光x,y,z的信息:
最后我們可以調用halcon的算子visualize_object_model_3d (WindowHandle, ObjectModel3DID, CameraParam1, PoseIn, 'color', 'blue', 'Reconstructed Connection Rod', '', Instructions, PoseOut) 將3d零件模型重建
模型可以通過鼠標隨意移動,就跟我上篇博客 opengl導入3d模型並且顯示一樣OpenGl讀取導入3D模型並且添加鼠標移動旋轉顯示
2.激光三角測量
激光三角測距法作為低成本的激光雷達設計方案,可獲得高精度、高性價比的應用效果,並成為室內服務機器人導航的首選方案,本文將對激光雷達核心組件進行介紹並重點闡述基於激光三角測距法的激光雷達原理。
激光雷達四大核心組件
激光雷達主要由激光器、接收器、信號處理單元和旋轉機構這四大核心組件構成。
激光器:激光器是激光雷達中的激光發射機構。在工作過程中,它會以脈沖的方式點亮。以思嵐科技的RPLIDAR A3系列雷達為例,每秒鍾,它會點亮和熄滅16000次。
接收器:激光器發射的激光照射到障礙物以后,通過障礙物的反射,反射光線會經由鏡頭組匯聚到接收器上。
信號處理單元:信號處理單元負責控制激光器的發射,以及接收器收到的信號的處理。根據這些信息計算出目標物體的距離信息。
旋轉機構:以上3個組件構成了測量的核心部件。旋轉機構負責將上述核心部件以穩定的轉速旋轉起來,從而實現對所在平面的掃描,並產生實時的平面圖信息。
激光三角測距法原理
目前激光雷達的測量原理主要有脈沖法、相干法和三角法3種,脈沖法和相干光法對激光雷達的硬件要求高,但測量精度比激光三角法要高得多,故多用於軍事領域。而激光三角測距法因其成本低,精度滿足大部分商用及民用要求,故得到了廣泛關注。
激光三角測距法主要是通過一束激光以一定的入射角度照射被測目標,激光在目標表面發生反射和散射,在另一角度利用透鏡對反射激光匯聚成像,光斑成像在CCD(Charge-coupled Device,感光耦合組件)位置傳感器上。當被測物體沿激光方向發生移動時,位置傳感器上的光斑將產生移動,其位移大小對應被測物體的移動距離,因此可通過算法設計,由光斑位移距離計算出被測物體與基線的距離值。由於入射光和反射光構成一個三角形,對光斑位移的計算運用了幾何三角定理,故該測量法被稱為激光三角測距法。
按入射光束與被測物體表面法線的角度關系,激光三角測距法可分為斜射式和直射式兩種。
1、直射式激光三角測距法
如圖1所示,當激光光束垂直入射被測物體表面,即入射光線與被測物體表面法線共線時,為直射式激光三角法。
2、斜射式激光三角測距法
當光路系統中,激光入射光束與被測物體表面法線夾角小於90°時,該入射方式即為斜射式。如圖2所示的光路圖為激光三角法斜射式光路圖。
由激光器發射的激光與物體表面法線成一定角度入射到被測物體表面,反(散)射光經B處的透鏡匯聚成像,最后被光敏單元采集。
由圖2可知入射光AO與基線AB的夾角為α,AB為激光器中心與CCD中心的距離,BF為透鏡的焦距f,D為被測物體距離基線無窮遠處時反射光線在光敏單元上成像的極限位置。DE為光斑在光敏單元上偏離極限位置的位移,記為x。當系統的光路確定后,α、AB與f均為已知參數。由光路圖中的幾何關系可知△ABO∽△DEB,則有邊長關系:
則易知
在確定系統的光路時,可將CCD位置傳感器的一個軸與基線AB平行(假設為y軸),則由通過算法得到的激光光點像素坐標為(Px,Py)可得到x的值為:
其中CellSize是光敏單元上單個像素的尺寸,DeviationValue是通過像素點計算的投影距離和實際投影距離x的偏差量。當被測物體與基線AB產生相對位移時,x改變為x,由以上條件可得被測物體運動距離y為:
單點激光測距原理
單點激光測距原理圖如下圖2-6所示,
激光頭Laser與攝像頭在同一水平線(稱為基准線)上,其距離為s,攝像頭焦距為f
假設目標物體Object在點狀激光器的照射下,反射回攝像頭成像平面的位置為點P
圖: 單點激光測距示意圖
由幾何知識可作相似三角形,激光頭、攝像頭與目標物體組成的三角形,相似於攝像頭、成像點P
設 P
f/x=q/s ==> q=fs/x
可分為兩部分計算:
X=x1+x2= f/tanβ + pixelSize* position
其中pixelSize是像素單位大小, position是成像的像素坐標相對於成像中心的位置。
最后,可求得距離d:
d=q/sinβ
3.代碼注解
看注釋就好了,慢慢看,結合示例跑一下就能大概理解了,示例在halcon的激光三角測量
如果在看的過程中對某個算子不理解,參數有疑問,可以直接雙擊那個算子
,打開幫助手冊,去看每個算子的參數信息,以及用法介紹:
一般dev_update_off放在開始,如果原來的程序有殘留一些窗口什么的就可以關閉,dev_update_on放在程序結束
dev_update_window:定義 程序執行打開和關閉期間,圖像對象是否在圖形窗口中顯示;在單步模式下,該規則無效,單個算子調用以后,對象總是顯示在圖形窗口上;在測量一系列算子的運行時間的時候,應該設置為OFF,以減少HDevelop中GUI更新的運行時間的影響
dev_update_pc:在程序執行期間,控制程序計數器的更新
dev_update_var:在程序執行期間控制變量窗口的更新或關閉,則每當程序修改變量時,更改變量窗口(圖標和控件變量)的內容。 dev_update_time:控制是否顯示算子的執行時間
* 首先,創建一個片光模型,並設置合適的參數,接下來連續采集一系列輪廓圖像。 * 最后,從模型中檢索視覺差圖像,分數圖像,標定坐標X,Y和Z以及測量得到的3D對象模型並顯示。 * dev_update_off () //暫停熟悉 read_image (ProfileImage, 'sheet_of_light/connection_rod_001') //讀取圖像 dev_close_window () //關閉窗體 dev_open_window_fit_image (ProfileImage, 0, 0, 1024, 768, WindowHandle1) //打開一個新窗體 set_display_font (WindowHandle1, 14, 'mono', 'true', 'false')//設置字體 dev_set_draw ('margin') //設置輪廓 dev_set_line_width (3) //設置線寬 dev_set_color ('green') //設置顏色為綠色 dev_set_lut ('default') // * * 設置計算校准測量所需的姿勢和相機參數 * 內部相機參數 CamParam := [0.0126514,640.275,-2.07143e+007,3.18867e+011,-0.0895689,0.0231197,6.00051e-006,6e-006,387.036,120.112,752,240] CamPose := [-0.00164029,1.91372e-006,0.300135,0.575347,0.587877,180.026,0] //相機坐標 LightplanePose := [0.00270989,-0.00548841,0.00843714,66.9928,359.72,0.659384,0] //片光平面坐標 MovementPose := [7.86235e-008,0.000120112,1.9745e-006,0,0,0,0] // * * 創建模型以處理配置文件圖像並設置模型所需的參數 gen_rectangle1 (ProfileRegion, 120, 75, 195, 710) //創建矩形 * 創建一個基於 3D 測量的片光模型 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', CamParam) //相機內部參數 set_sheet_of_light_param (SheetOfLightModelID, 'camera_pose', CamPose) //相機坐標系統 set_sheet_of_light_param (SheetOfLightModelID, 'lightplane_pose', LightplanePose) //相機姿態,如果與測的物體相同平面 set_sheet_of_light_param (SheetOfLightModelID, 'movement_pose', MovementPose) //移動姿態,移動過程中通常為大地坐標 * * 連續的圖像測量輪廓 for Index := 1 to 290 by 1 read_image (ProfileImage, 'sheet_of_light/connection_rod_' + Index$'.3') //讀取圖像 dev_display (ProfileImage) //顯示圖像 dev_display (ProfileRegion) //顯示區域 measure_profile_sheet_of_light (ProfileImage, SheetOfLightModelID, []) //對輸入和存儲的輪廓圖像進行片光技術處理 disp_message (WindowHandle1, '采集輪廓圖像', 'window', -1, -1, 'black', 'true') //顯示信息 endfor * 獲取片光圖像 get_sheet_of_light_result (Disparity, SheetOfLightModelID, 'disparity') //返回片光深度距離 get_sheet_of_light_result (X, SheetOfLightModelID, 'x') //返回片光x數據 get_sheet_of_light_result (Y, SheetOfLightModelID, 'y') //返回片光y數據 get_sheet_of_light_result (Z, SheetOfLightModelID, 'z') //返回片光z數據 get_sheet_of_light_result_object_model_3d (SheetOfLightModelID, ObjectModel3DID) //返回片光3D模型數據 clear_sheet_of_light_model (SheetOfLightModelID) //清除指定片光模型 * * 顯示視差圖像 get_image_size (Disparity, Width, Height) //獲取圖像大小 dev_set_window_extents (0, 0, Width, Height) //調整大小 dev_set_lut ('temperature') // set_display_font (WindowHandle1, 16, 'mono', 'true', 'false') //設置字體 dev_clear_window () //清除窗體 dev_display (Disparity) disp_message (WindowHandle1, '重建片光生產的視差圖像', 'window', -1, -1, 'black', 'true') //顯示信息 disp_continue_message (WindowHandle1, 'black', 'true') //顯示暫停信息 stop () //暫停 * * 顯示Z坐標 dev_close_window () //關閉窗體 dev_open_window (Height + 10, 0, Width * .5, Height * .5, 'black', WindowHandle3) //打開窗體3 set_display_font (WindowHandle3, 16, 'mono', 'true', 'false')//設置字體 dev_display (Z) //顯示Z信息 disp_message (WindowHandle3, '標定的 Z 坐標', 'window', -1, -1, 'black', 'true') //顯示信息 * * 顯示Y坐標 dev_open_window ((Height + 10) * .5, 0, Width * .5, Height * .5, 'black', WindowHandle2) //打開窗體2 set_display_font (WindowHandle2, 16, 'mono', 'true', 'false')//設置字體 dev_display (Y)//顯示Z信息 disp_message (WindowHandle2, '標定的 Y 坐標', 'window', -1, -1, 'black', 'true') * * 顯示X坐標 dev_open_window (0, 0, Width * .5, Height * .5, 'black', WindowHandle1) //打開窗體1 dev_display (X)//顯示Z信息 dev_set_lut ('default') set_display_font (WindowHandle1, 16, 'mono', 'true', 'false')//設置字體 disp_message (WindowHandle1, '標定的 X 坐標', 'window', -1, -1, 'black', 'true') //顯示信息 disp_continue_message (WindowHandle3, 'black', 'true') //顯示暫停信息 stop () //暫停 * * Display the 3d object model CameraParam1 := [0.012,0,6e-006,6e-006,376,240,752,480] //相機內部參數 Instructions[0] := '旋轉: Left 鼠標左鍵' Instructions[1] := '縮放: Shift + 鼠標左鍵' Instructions[2] := '移動Move: Ctrl + 鼠標左鍵' PoseIn := [0,-10,300,-30,0,-30,0] dev_close_window () //關閉窗體2 dev_close_window () //關閉窗體3 dev_close_window () //關閉窗體12 dev_open_window (0, 0, CameraParam1[6], CameraParam1[7], 'black', WindowHandle) //打開新窗體 set_display_font (WindowHandle, 16, 'mono', 'true', 'false')//設置字體 visualize_object_model_3d (WindowHandle, ObjectModel3DID, CameraParam1, PoseIn, 'color', \ 'blue', '重建', '', Instructions, PoseOut)//顯示信息 * 清除片光模型 clear_object_model_3d (ObjectModel3DID)
這上面有提到一個視差圖像:
Disparity(視差)怎么理解?
在研究雙目深度圖估計時,經常會使用D=B×f/d(D:Depth,B:Baseline,f:focal,d:disparity)這個公式,從視差推理出深度,那么這里的d到底怎么理解?
現在,伸出你左右手的食指,放在離眼睛不同距離的位置上。先閉上左眼看兩只手指,再閉上右眼觀察兩只手指,可以發現,左右眼看到的東西是不一樣的,其次,**距離眼睛近的物體移動的距離(視差)更大,距離眼睛遠的物體移動的距離(視差)更小。**將同一空間物理點在不同圖像中的映像點對應起來,這個差別,我們稱作視差(Disparity)圖像。參考下圖:
公式不難發現,視差與深度成反比,關系如下:
有了視差disparity,就可以推斷出深度圖。深度圖像也稱為距離圖像,是指將相機到場景中各點的距離(深度)值作為像素值的圖像。深度圖獲取方法有很多,例如:激光雷達深度成像法、計算機立體視覺成像、坐標測量機法、莫爾條紋法、結構光法等。
舉個例子:(Kinect相機)
參考博客:Disparity(視差)簡單解釋 鏈接: https://blog.csdn.net/weixin_40367126/article/details/90753760
參考博客:HALCON例子:激光三角ReconstructConnectionRodCalib 鏈接: https://weibo.com/ttarticle/p/show?id=2309404407678905483355
若有興趣交流分享技術,可關注本人公眾號,里面會不定期的分享各種編程教程,和共享源碼,諸如研究分享關於c/c++,python,前端,后端,opencv,halcon,opengl,機器學習深度學習之類有關於基礎編程,圖像處理和機器視覺開發的知識