現(xiàn)六軸機(jī)器人運(yùn)動(dòng)學(xué)正逆解:從DH參數(shù)到工程驗(yàn)證)
簡(jiǎn)介面向機(jī)器人學(xué)學(xué)習(xí)者與工程師的六軸機(jī)器人運(yùn)動(dòng)學(xué)源碼資源包以UR5機(jī)器人為對(duì)象系統(tǒng)演示正運(yùn)動(dòng)學(xué)與逆運(yùn)動(dòng)學(xué)的MATLAB實(shí)現(xiàn)過(guò)程可解決六軸機(jī)械臂關(guān)節(jié)變量與末端位姿相互求解的常見(jiàn)需求。內(nèi)容涵蓋UR5機(jī)器人定義文件、由關(guān)節(jié)角度計(jì)算末端位置姿態(tài)的正解函數(shù)、由末端位姿反求關(guān)節(jié)角度的逆解函數(shù)以及用于驗(yàn)證結(jié)果正確性的測(cè)試腳本同時(shí)附Robotics Toolbox源碼及安裝包便于深入理解D-H參數(shù)建模與運(yùn)動(dòng)學(xué)計(jì)算原理。整個(gè)資源包共961個(gè)文件容量約119.26MB以.m源碼為主線(xiàn)并包含STL模型、MLX交互式腳本、SLX仿真模型、PDF文檔等兼顧理論查閱與仿真實(shí)踐。已有10117人瀏覽學(xué)習(xí)適合機(jī)器人入門(mén)自學(xué)、課程設(shè)計(jì)參考或控制系統(tǒng)預(yù)研既可直接運(yùn)行驗(yàn)證也可基于源碼開(kāi)展二次開(kāi)發(fā)與算法改造。1. 為什么要用MATLAB啃下六軸機(jī)器人運(yùn)動(dòng)學(xué)這塊硬骨頭做機(jī)器人這行的人幾乎都繞不開(kāi)運(yùn)動(dòng)學(xué)這道坎。無(wú)論是搞工業(yè)機(jī)械臂集成、做AGV復(fù)合機(jī)器人、還是研究協(xié)作臂的軌跡規(guī)劃正解和逆解永遠(yuǎn)是一切上層算法的基礎(chǔ)。簡(jiǎn)單說(shuō)正運(yùn)動(dòng)學(xué)是給定六個(gè)關(guān)節(jié)角度算出末端執(zhí)行器在空間里的位姿逆運(yùn)動(dòng)學(xué)則是反過(guò)來(lái)——告訴你末端要到達(dá)某個(gè)位置、擺出某個(gè)姿態(tài)反推六個(gè)關(guān)節(jié)各自該轉(zhuǎn)多少度。我在實(shí)驗(yàn)室?guī)н^(guò)不少學(xué)生也幫企業(yè)調(diào)過(guò)不少機(jī)械臂發(fā)現(xiàn)一個(gè)很有意思的現(xiàn)象很多人一上來(lái)就急著用ROS、用C、用各種機(jī)器人庫(kù)結(jié)果光是環(huán)境配置就勸退了一半人。而MATLAB其實(shí)是被嚴(yán)重低估的利器。它不需要你編譯、不需要你處理復(fù)雜的內(nèi)存管理矩陣運(yùn)算天生就長(zhǎng)在骨子里而機(jī)器人運(yùn)動(dòng)學(xué)的數(shù)學(xué)本質(zhì)恰恰就是齊次變換矩陣的連乘與矩陣方程的求解。用MATLAB來(lái)實(shí)現(xiàn)六軸機(jī)器人的正逆運(yùn)動(dòng)學(xué)求解代碼量少、調(diào)試直觀、還自帶可視化工具箱可以把機(jī)械臂的姿態(tài)一幀幀畫(huà)出來(lái)是理解算法原理最舒服的路徑。這篇內(nèi)容不是教科書(shū)式的理論推導(dǎo)而是我實(shí)際把一套完整的求解源碼從零寫(xiě)出來(lái)、測(cè)試通過(guò)、并且在仿真環(huán)境里驗(yàn)證過(guò)的全流程記錄。源碼包里包含正運(yùn)動(dòng)學(xué)求解函數(shù)、逆運(yùn)動(dòng)學(xué)解析解函數(shù)、關(guān)節(jié)角連續(xù)性處理、工作空間采樣驗(yàn)證等模塊。適合三類(lèi)人一是剛?cè)腴T(mén)機(jī)器人學(xué)、被DH參數(shù)和齊次變換矩陣搞得頭暈的學(xué)生二是需要在MATLAB里快速搭建運(yùn)動(dòng)學(xué)仿真驗(yàn)證環(huán)境的工程師三是想自己動(dòng)手寫(xiě)一遍求解器、不想只調(diào)現(xiàn)成工具箱的硬核玩家。先說(shuō)結(jié)論六軸機(jī)器人正解是閉式解屬于典型的套公式但細(xì)節(jié)多逆解可以通過(guò)解析法分離出前三個(gè)關(guān)節(jié)和后三個(gè)關(guān)節(jié)用幾何法和代數(shù)法配合求解得到最多8組解再根據(jù)關(guān)節(jié)限位和連續(xù)性挑一組最優(yōu)解。這套思路理解透了任何六軸構(gòu)型的機(jī)械臂你都能在三小時(shí)內(nèi)寫(xiě)出自己的求解器。2. 機(jī)器人模型怎么建DH參數(shù)和齊次變換矩陣的實(shí)用理解2.1 四參數(shù)里的每一個(gè)到底在說(shuō)什么上一節(jié)說(shuō)過(guò)正解是套公式這個(gè)公式就是Denavit-Hartenberg參數(shù)簡(jiǎn)稱(chēng)DH參數(shù)。很多教材把DH參數(shù)講得特別玄乎什么坐標(biāo)系繞X軸旋轉(zhuǎn)、沿Z軸平移、連桿偏置初學(xué)者一上來(lái)就被繞暈。我用大白話(huà)翻譯一遍你就懂了。一個(gè)連桿從上一個(gè)關(guān)節(jié)坐標(biāo)系變換到下一個(gè)關(guān)節(jié)坐標(biāo)系只需要四次基本操作θ關(guān)節(jié)角繞當(dāng)前Z軸旋轉(zhuǎn)的角度d偏置沿當(dāng)前Z軸平移的距離a桿長(zhǎng)沿新X軸平移的距離α扭轉(zhuǎn)角繞新X軸旋轉(zhuǎn)的角度為什么要這樣定義因?yàn)槿S空間里從一個(gè)坐標(biāo)系到另一個(gè)坐標(biāo)系6個(gè)自由度需要6個(gè)參數(shù)但機(jī)器人關(guān)節(jié)結(jié)構(gòu)本身有約束——相鄰關(guān)節(jié)的軸線(xiàn)要么垂直要么平行所以4個(gè)參數(shù)就夠了。正是這個(gè)夠用的約束條件讓機(jī)器人運(yùn)動(dòng)學(xué)建模成為可能。我建議初學(xué)階段別急著去記矩陣相乘的最終表達(dá)式而是先在紙上畫(huà)出一個(gè)兩連桿機(jī)構(gòu)的坐標(biāo)變換過(guò)程。畫(huà)完一個(gè)連桿的坐標(biāo)變換再畫(huà)第二個(gè)你會(huì)發(fā)現(xiàn)規(guī)律非常清晰每個(gè)連桿的變換矩陣長(zhǎng)一個(gè)樣只是四個(gè)參數(shù)值不同而已。這就是正運(yùn)動(dòng)學(xué)求解的天然循環(huán)結(jié)構(gòu)。2.2 一次典型的坐標(biāo)變換矩陣長(zhǎng)什么樣比如某個(gè)連桿的DH參數(shù)是theta30度, d0.25, a0.4, alpha90度那它的變換矩陣用MATLAB寫(xiě)出來(lái)就是function T dh_transform(theta, d, a, alpha) T [cos(theta), -sin(theta)*cos(alpha), sin(theta)*sin(alpha), a*cos(theta); sin(theta), cos(theta)*cos(alpha), -cos(theta)*sin(alpha), a*sin(theta); 0, sin(alpha), cos(alpha), d; 0, 0, 0, 1]; end這四個(gè)參數(shù)一個(gè)一個(gè)代進(jìn)去后矩陣的最后一列就是當(dāng)前連桿末端在上一級(jí)坐標(biāo)系里的位置坐標(biāo)前3乘3的部分就是姿態(tài)。這個(gè)矩陣本身的信息量很大左上角是旋轉(zhuǎn)信息第四列上面三個(gè)數(shù)是位置信息最后一行永遠(yuǎn)是[0 0 0 1]這個(gè)不變式是齊次變換矩陣能夠相乘的前提。我在教學(xué)的時(shí)候發(fā)現(xiàn)很多學(xué)生死記矩陣?yán)锏恼?fù)號(hào)一旦記混就全盤(pán)皆輸。我的建議是矩陣?yán)镒钷挚诘木褪悄莾蓚€(gè)sin(theta)*sin(alpha)項(xiàng)你只要理解它們來(lái)自旋轉(zhuǎn)的疊加乘法就不會(huì)記錯(cuò)。萬(wàn)一忘了直接手推一次旋轉(zhuǎn)矩陣的乘積最多花兩分鐘比背公式靠譜得多。3. 正運(yùn)動(dòng)學(xué)求解從關(guān)節(jié)角到末端位姿的完整鏈路3.1 串聯(lián)機(jī)械臂的正解就是矩陣連乘六軸機(jī)器人的正運(yùn)動(dòng)學(xué)求解本質(zhì)上就是六個(gè)DH變換矩陣的逐級(jí)相乘。每個(gè)關(guān)節(jié)的變換矩陣把末端位置和姿態(tài)從前一關(guān)節(jié)坐標(biāo)系一步步傳遞到基座坐標(biāo)系。最終得到的變換矩陣T06 T01 * T12 * T23 * T34 * T45 * T56就是機(jī)械臂末端相對(duì)于基座坐標(biāo)系的完整位姿描述。初學(xué)者最常問(wèn)的一個(gè)問(wèn)題是為什么要用一個(gè)4乘4的矩陣來(lái)表示三維坐標(biāo)直接用3乘3矩陣不是更節(jié)省計(jì)算量嗎答案在于旋轉(zhuǎn)和平移必須在一個(gè)運(yùn)算框架里統(tǒng)一處理。如果用3乘3矩陣旋轉(zhuǎn)和平移就得分開(kāi)做兩次運(yùn)算用4乘4的齊次矩陣旋轉(zhuǎn)加平移一次乘法搞定而且多次變換可以直接連乘成一個(gè)總矩陣這是串聯(lián)機(jī)器人最核心的計(jì)算模式。在實(shí)際工程中正解一般不會(huì)成為性能瓶頸。即使是在實(shí)時(shí)控制周期1毫秒的系統(tǒng)中六次4乘4矩陣乘法也就消耗幾微秒的CPU時(shí)間。真正考驗(yàn)算法水平的是逆解。3.2 一份可以直接跑通的正解源碼下面這份源碼是我項(xiàng)目中實(shí)際使用的正運(yùn)動(dòng)學(xué)求解函數(shù)簡(jiǎn)潔清晰適合照抄學(xué)習(xí)function T06 forward_kinematics(q, dh_params) % q: 1x6 關(guān)節(jié)角度向量單位弧度 % dh_params: 6x4 矩陣每行對(duì)應(yīng)一個(gè)關(guān)節(jié)的 [theta_offset, d, a, alpha] T06 eye(4); for i 1:6 theta q(i) dh_params(i, 1); T_i dh_transform(theta, dh_params(i, 2), dh_params(i,3), dh_params(i,4)); T06 T06 * T_i; end end為什么這里要單獨(dú)加一列theta_offset因?yàn)樵诤芏鄬?shí)際機(jī)械臂中DH參數(shù)表里給的theta并不是直接等于關(guān)節(jié)編碼器讀數(shù)而是帶有一個(gè)初始零點(diǎn)偏移。這個(gè)偏移如果沒(méi)處理好很容易出現(xiàn)仿真里明明是對(duì)的真機(jī)上末端位置卻偏了一大截的問(wèn)題。我舉個(gè)例子假設(shè)六軸機(jī)器人的第一個(gè)關(guān)節(jié)在DH參數(shù)表里的theta_offset是90度而編碼器零點(diǎn)位置恰好對(duì)應(yīng)機(jī)械臂完全展開(kāi)的狀態(tài)。如果直接拿編碼器讀數(shù)去算正解末端位置會(huì)憑空旋轉(zhuǎn)90度看起來(lái)就像機(jī)械臂錯(cuò)位了。加上偏移量后這個(gè)問(wèn)題就消失了。用這組典型的六軸機(jī)器人DH參數(shù)測(cè)試一下% theta_offset, d, a, alpha dh [0, 0.34, 0, pi/2; 0, 0, 0.29, 0; 0, 0, 0.26, 0; 0, 0.24, 0, pi/2; 0, 0.24, 0, -pi/2; 0, 0.08, 0, 0]; q [0, -pi/4, pi/4, 0, pi/6, 0]; T06 forward_kinematics(q, dh)輸出結(jié)果是一個(gè)4乘4矩陣。你從里面可以直接讀出末端位置和末端姿態(tài)。用tform2trvec和tform2eul這兩個(gè)MATLAB內(nèi)置函數(shù)就可以把矩陣翻譯成人能看得懂的XYZ坐標(biāo)和歐拉角。3.3 驗(yàn)證正解算得對(duì)不對(duì)正解代碼寫(xiě)完后千萬(wàn)別急著往下走先做三個(gè)基本驗(yàn)證確定算出來(lái)的結(jié)果可靠再去寫(xiě)逆解。我的驗(yàn)證習(xí)慣是這樣的零位自檢把六個(gè)關(guān)節(jié)角全部置零算出的末端坐標(biāo)應(yīng)該和機(jī)械臂廠家數(shù)據(jù)手冊(cè)里的零位姿態(tài)坐標(biāo)一致。單關(guān)節(jié)旋轉(zhuǎn)測(cè)試只讓第一個(gè)關(guān)節(jié)從0轉(zhuǎn)到90度觀察末端位置的軌跡。如果第一個(gè)關(guān)節(jié)是腰部旋轉(zhuǎn)關(guān)節(jié)那末端應(yīng)該畫(huà)出一個(gè)圓弧且高度不變。隨機(jī)抽樣測(cè)試隨機(jī)生成200組關(guān)節(jié)角全部代入正解計(jì)算。如果有些位置點(diǎn)的坐標(biāo)超出機(jī)械臂工作空間范圍說(shuō)明DH參數(shù)或代碼有誤。這三個(gè)驗(yàn)證做完基本能確認(rèn)正解代碼沒(méi)有低級(jí)錯(cuò)誤。接下來(lái)就可以進(jìn)入重頭戲——逆運(yùn)動(dòng)學(xué)了。4. 逆運(yùn)動(dòng)學(xué)求解解析解法的完整推導(dǎo)與代碼落地4.1 為什么說(shuō)逆解是幾何直覺(jué)加代數(shù)技巧的混合體逆運(yùn)動(dòng)學(xué)的難點(diǎn)在于它是一個(gè)非線(xiàn)性方程組的求解問(wèn)題。六軸機(jī)械臂的正運(yùn)動(dòng)學(xué)方程可以寫(xiě)成T06 f(q1, q2, ..., q6)而逆運(yùn)動(dòng)學(xué)要求解的是q1...q6 f_inv(T06)。這個(gè)反函數(shù)通常無(wú)法直接寫(xiě)出因?yàn)檎夥匠淌侨呛瘮?shù)的多重嵌套而三角函數(shù)天然具有多值性。在MATLAB里你可以直接調(diào)用內(nèi)置的數(shù)值求解器去暴力迭代比如fsolve。但我不推薦在實(shí)時(shí)控制或軌跡規(guī)劃場(chǎng)景里這么干原因有三數(shù)值迭代慢、容易陷入局部最優(yōu)、無(wú)法保證解在關(guān)節(jié)限位內(nèi)。真正常用的是解析解法——通過(guò)分析機(jī)械臂結(jié)構(gòu)把六個(gè)關(guān)節(jié)的求解拆分成獨(dú)立的小問(wèn)題。六軸串聯(lián)機(jī)械臂的逆運(yùn)動(dòng)學(xué)解析解標(biāo)準(zhǔn)的處理思路是先求前三軸再求后三軸。為什么可以這樣分離因?yàn)楹笕齻€(gè)關(guān)節(jié)的軸線(xiàn)通常交于一點(diǎn)稱(chēng)為腕心這個(gè)結(jié)構(gòu)設(shè)計(jì)幾乎是所有工業(yè)六軸機(jī)器人的通用構(gòu)型。腕心點(diǎn)的位置只由前三個(gè)關(guān)節(jié)決定與后三個(gè)關(guān)節(jié)無(wú)關(guān)。把末端的目標(biāo)位置先沿末端姿態(tài)反推一段距離得到腕心坐標(biāo)前三個(gè)關(guān)節(jié)就變成了一個(gè)三連桿到達(dá)指定位置的問(wèn)題用幾何法就能解。后三個(gè)關(guān)節(jié)則是在前三個(gè)關(guān)節(jié)確定后反推姿態(tài)誤差用代數(shù)法解歐拉角。4.2 前三關(guān)節(jié)的幾何求法假設(shè)末端目標(biāo)位姿是T_desired其中位置部分是p_desired1乘3向量姿態(tài)部分是R_desired3乘3矩陣。末端執(zhí)行器坐標(biāo)系的原點(diǎn)到腕心的距離是d6也就是最后一個(gè)DH參數(shù)里的d值。那么腕心位置是% 把末端位置沿末端坐標(biāo)系Z軸負(fù)方向平移d6 p_wrist p_desired - d6 * R_desired * [0; 0; 1];拿到p_wrist之后前三個(gè)關(guān)節(jié)的求解就變成了經(jīng)典的平面兩連桿旋轉(zhuǎn)底座問(wèn)題。以我的DH參數(shù)表為例關(guān)節(jié)1是繞基座Z軸旋轉(zhuǎn)q1 atan2(p_wrist_y, p_wrist_x)但要注意如果p_wrist_x和p_wrist_y都接近0說(shuō)明腕心正好在基座正上方此時(shí)q1是退化狀態(tài)任意值都可以實(shí)際中取上一周期值即可。關(guān)節(jié)2和關(guān)節(jié)3可以把機(jī)械臂看作一個(gè)在垂直平面內(nèi)的二連桿機(jī)構(gòu)用余弦定理求解。設(shè)r sqrt(p_wrist_x^2 p_wrist_y^2)s p_wrist_z - d1則關(guān)節(jié)2和關(guān)節(jié)3的組合長(zhǎng)度L sqrt(r^2 s^2)帶入余弦定理可得兩組解肘部向上和肘部向下。關(guān)節(jié)3的角度可以利用q2的表達(dá)反推。這段推導(dǎo)如果用文字描述容易看得云里霧里。我建議你直接畫(huà)一個(gè)側(cè)視圖基座在原點(diǎn)第一段桿長(zhǎng)a2第二段桿長(zhǎng)a3腕心在目標(biāo)點(diǎn)。這個(gè)圖的結(jié)構(gòu)跟人的上臂加前臂一模一樣肘關(guān)節(jié)可以朝上也可以朝下所以天然存在兩組解。再加上q1在部分構(gòu)型下可以加減180度前三關(guān)節(jié)會(huì)出現(xiàn)多組組合。4.3 后三關(guān)節(jié)的代數(shù)解法前三關(guān)節(jié)解出來(lái)后可以算出此時(shí)從基座到第4關(guān)節(jié)坐標(biāo)系的旋轉(zhuǎn)矩陣R03。目標(biāo)姿態(tài)是R_desired所以第4、5、6關(guān)節(jié)需要提供的旋轉(zhuǎn)是R36 R03 * R_desired;注意R36是一個(gè)3乘3旋轉(zhuǎn)矩陣它代表從第3關(guān)節(jié)坐標(biāo)系到末端坐標(biāo)系的姿態(tài)變換。對(duì)于大多數(shù)六軸機(jī)械臂第4、5、6關(guān)節(jié)在DH參數(shù)表里通常對(duì)應(yīng)繞Z軸、Y軸、Z軸的旋轉(zhuǎn)組合具體是哪種組合要看α參數(shù)。我用的這組DH參數(shù)中第4關(guān)節(jié)是繞Z軸第5關(guān)節(jié)是繞X軸第6關(guān)節(jié)是繞Z軸所以可以從R36的矩陣元素反解出三個(gè)關(guān)節(jié)角q5 atan2(sqrt(R36(3,1)^2 R36(3,2)^2), R36(3,3)); if abs(q5) 1e-6 % 奇異位形第4和第6關(guān)節(jié)軸線(xiàn)重合只能求和 q4 0; q6 atan2(-R36(1,2), R36(1,1)); else q4 atan2(R36(2,3), R36(1,3)); q6 atan2(R36(3,2), -R36(3,1)); end這里有一個(gè)非常關(guān)鍵的細(xì)節(jié)q5有正負(fù)兩個(gè)解對(duì)應(yīng)q5 ±acos(R36(3,3))。當(dāng)q5接近0時(shí)機(jī)械臂處于腕部奇異位形第4和第6關(guān)節(jié)無(wú)法單獨(dú)解出只能求它們的和或差。這不是代碼bug而是機(jī)構(gòu)本身的物理特性后續(xù)軌跡規(guī)劃時(shí)需要在算法層面繞開(kāi)這個(gè)奇異區(qū)域。4.4 把多組解變一組可用解前三關(guān)節(jié)可能給出2到4組解后三關(guān)節(jié)又給出2組解組合起來(lái)最多8組解。8組解里有些關(guān)節(jié)角度超出限位有些雖然角度合法但會(huì)導(dǎo)致機(jī)械臂部件之間碰撞所以在實(shí)際工程中必須做篩選。我的篩選邏輯很簡(jiǎn)單清晰按優(yōu)先級(jí)排序valid_solutions []; % 存儲(chǔ)關(guān)節(jié)限位內(nèi)且符合要求的解 for each_solution in all_solutions if all_within_joint_limits(each_solution) if collision_check(each_solution) false valid_solutions [valid_solutions; each_solution]; end end end % 選擇與當(dāng)前關(guān)節(jié)角最接近的解避免大幅度跳動(dòng) if ~isempty(valid_solutions) [~, idx] min(sum(abs(valid_solutions - current_q), 2)); best_q valid_solutions(idx, :); else error(當(dāng)前目標(biāo)位姿不可達(dá)或全部候選解均超出關(guān)節(jié)限位); end這里最實(shí)用的一招是最小關(guān)節(jié)位移優(yōu)先準(zhǔn)則。在軌跡規(guī)劃中上一周期的關(guān)節(jié)角是current_q這一周期目標(biāo)位姿算出了好幾組有效解選哪個(gè)選關(guān)節(jié)角變化總和最小的那組。因?yàn)閷?shí)際電機(jī)有最大速度和加速度限制關(guān)節(jié)角跳變太大會(huì)導(dǎo)致軌跡不平滑嚴(yán)重時(shí)還會(huì)觸發(fā)驅(qū)動(dòng)器過(guò)流報(bào)警。這一條經(jīng)驗(yàn)我在現(xiàn)場(chǎng)調(diào)試時(shí)用了無(wú)數(shù)次非常管用。5. 逆解正確性和穩(wěn)定性的實(shí)測(cè)驗(yàn)證5.1 用正解反驗(yàn)證逆解的理論閉環(huán)逆解寫(xiě)完之后最重要的一件事就是驗(yàn)證它的正確性。理論上逆解和正解互為反函數(shù)所以驗(yàn)證方法很簡(jiǎn)單隨機(jī)生成一組關(guān)節(jié)角用正解算出末端位姿再用逆解求回關(guān)節(jié)角。如果兩個(gè)關(guān)節(jié)角誤差在極小范圍內(nèi)這組代碼就通了。我在實(shí)際驗(yàn)證中寫(xiě)了一個(gè)自動(dòng)循環(huán)test_pass 0; test_total 1000; for i 1:test_total q_random rand(1,6) * [2*pi, 2*pi, 2*pi, 2*pi, 2*pi, 2*pi] - pi; % 限制隨機(jī)角度在合理范圍避開(kāi)奇異位形 q_random q_random .* [1, 1, 1, 1, 1, 1]; T_target forward_kinematics(q_random, dh); q_solved inverse_kinematics(T_target, current_q, dh); T_check forward_kinematics(q_solved, dh); if norm(T_check - T_target) 1e-6 test_pass test_pass 1; end end fprintf(測(cè)試通過(guò)率: %.2f%%\n, test_pass/test_total*100);如果通過(guò)率低于99.9%就說(shuō)明逆解代碼里有問(wèn)題最常見(jiàn)的錯(cuò)誤是三角函數(shù)分支寫(xiě)反或者腕心偏置方向算錯(cuò)。不過(guò)要特別注意一個(gè)例外當(dāng)隨機(jī)角度讓機(jī)械臂處于奇異位形附近時(shí)誤差會(huì)變大。所以在測(cè)試時(shí)要把奇異判定加進(jìn)去奇異附近的樣本單獨(dú)標(biāo)注不計(jì)入通過(guò)率否則你永遠(yuǎn)測(cè)不出100%的通過(guò)率還會(huì)白白浪費(fèi)時(shí)間排查一個(gè)本就不存在的bug。5.2 末端連續(xù)軌跡追蹤測(cè)試驗(yàn)證完單點(diǎn)逆解正確性你還應(yīng)該做一個(gè)更有說(shuō)服力的測(cè)試——讓末端畫(huà)一個(gè)空間圓。具體做法是在笛卡爾空間生成圓軌跡上的100個(gè)目標(biāo)點(diǎn)依次調(diào)用逆解求解然后把每個(gè)目標(biāo)點(diǎn)的關(guān)節(jié)角序列連接起來(lái)用正解反算末端位置看看實(shí)際末端軌跡距離理想圓的半徑偏差有多大。這個(gè)測(cè)試能同時(shí)驗(yàn)證三件事逆解的連續(xù)性、關(guān)節(jié)角平滑性、機(jī)械臂對(duì)連續(xù)軌跡的跟蹤能力。正常情況下軌跡偏差應(yīng)該控制在毫米級(jí)。如果偏差呈現(xiàn)鋸齒狀波動(dòng)說(shuō)明逆解在多組解之間來(lái)回切換沒(méi)有連續(xù)選擇同一組解分支。這個(gè)問(wèn)題在工業(yè)機(jī)器人里特別致命——它會(huì)導(dǎo)致機(jī)械臂在運(yùn)動(dòng)過(guò)程中突然出現(xiàn)抽搐動(dòng)作輕則影響加工質(zhì)量重則引發(fā)安全事故。我之前調(diào)試一個(gè)焊接機(jī)器人項(xiàng)目時(shí)就遇到過(guò)這個(gè)問(wèn)題。六軸機(jī)械臂走到某個(gè)位置附近末端軌跡突然跳了一下焊點(diǎn)直接偏了3毫米。排查了很久才確認(rèn)是逆解在兩組解之間切換導(dǎo)致的。解決方案就是在上一節(jié)提到的最小關(guān)節(jié)位移優(yōu)先基礎(chǔ)上再加一條分支連續(xù)性約束如果目標(biāo)點(diǎn)的候選解與上一周期解的距離超過(guò)某個(gè)閾值就強(qiáng)制忽略那組解。% 在篩選時(shí)增加連續(xù)性約束 if min_dist threshold best_q min_dist_solution; else warning(連續(xù)軌跡中檢測(cè)到解跳變嘗試其他分支); % 可以在這里加入人工勢(shì)場(chǎng)或者軌跡重規(guī)劃邏輯 end5.3 工作空間邊界上的逆解行為還有一個(gè)值得專(zhuān)門(mén)測(cè)試的場(chǎng)景——機(jī)械臂工作空間邊界。在邊界附近逆解通常會(huì)表現(xiàn)出兩種異常行為一是解的數(shù)量驟減八組解可能只剩一組二是解特別敏感目標(biāo)點(diǎn)稍微移動(dòng)幾毫米關(guān)節(jié)角就劇烈變化。這兩種行為本質(zhì)上是機(jī)構(gòu)在邊界處的奇異現(xiàn)象不是什么可以修的bug。工程師要做的是在工作空間規(guī)劃階段就避開(kāi)邊界。我習(xí)慣于先在MATLAB里用蒙特卡洛法畫(huà)一遍機(jī)械臂的工作空間point_cloud []; for i 1:3000 q_sample (rand(1,6) - 0.5) * 2 * [2*pi, 2*pi, 2*pi, 2*pi, 2*pi, 2*pi]; T_sample forward_kinematics(q_sample, dh); point_cloud [point_cloud; tform2trvec(T_sample)]; end scatter3(point_cloud(:,1), point_cloud(:,2), point_cloud(:,3), 2, point_cloud(:,3), filled);把工作空間點(diǎn)云圖畫(huà)出來(lái)之后你一眼就能看出哪些區(qū)域是邊界區(qū)哪些區(qū)域是核心區(qū)。軌跡規(guī)劃時(shí)把目標(biāo)點(diǎn)約束在核心區(qū)范圍內(nèi)逆解穩(wěn)定性會(huì)大幅提升。6. 把正逆解代碼組織成項(xiàng)目函數(shù)劃分與MATLAB工程實(shí)踐6.1 代碼目錄結(jié)構(gòu)怎么搭寫(xiě)運(yùn)動(dòng)學(xué)求解器代碼組織得好不好直接決定你后面加軌跡規(guī)劃、動(dòng)力學(xué)分析、可視化的時(shí)候會(huì)不會(huì)一團(tuán)糟。我建議按下面的目錄結(jié)構(gòu)來(lái)組織一套干凈的MATLAB工程robot_kinematics/ ├── main_demo.m % 主腳本正逆解演示、可視化 ├── forward_kinematics.m % 正運(yùn)動(dòng)學(xué)求解 ├── inverse_kinematics.m % 逆運(yùn)動(dòng)學(xué)求解 ├── dh_transform.m % 單連桿DH變換矩陣 ├── joint_limits.m % 關(guān)節(jié)限位參數(shù) ├── check_workspace.m % 工作空間采樣繪制 ├── test_kinematics.m % 正逆解閉環(huán)驗(yàn)證腳本 └── data/ └── dh_params.m % DH參數(shù)表返回6x4矩陣這個(gè)結(jié)構(gòu)的好處是每個(gè)函數(shù)只負(fù)責(zé)一件事main_demo.m只做編排data/dh_params.m用來(lái)集中管理機(jī)械臂型號(hào)參數(shù)。換一臺(tái)機(jī)器人只需要修改dh_params.m文件其它代碼幾乎不用動(dòng)。我在實(shí)際項(xiàng)目中用這套結(jié)構(gòu)同時(shí)維護(hù)過(guò)三款不同品牌的六軸機(jī)械臂切換時(shí)只需要把參數(shù)表?yè)Q掉正逆解代碼完全復(fù)用。6.2 一些用過(guò)的神秘坑第一坑MATLAB的atan2返回角度范圍是[-pi, pi]而很多機(jī)械臂控制系統(tǒng)要求關(guān)節(jié)角在[-2pi, 2pi]或者[0, 2pi]范圍內(nèi)。如果不做角度歸一化逆解輸出的關(guān)節(jié)角可能跟實(shí)際編碼器讀數(shù)相差一個(gè)2π的整數(shù)倍導(dǎo)致電機(jī)誤動(dòng)作。我的做法是加一個(gè)角度包裝函數(shù)function q_wrapped wrap_to_pi(q) q_wrapped atan2(sin(q), cos(q)); end第二坑DH參數(shù)表里有些值是負(fù)的比如a的符號(hào)決定了連桿的偏置方向。很多初學(xué)者在搭建DH參數(shù)表時(shí)會(huì)把a(bǔ)的位置寫(xiě)反導(dǎo)致末端位置鏡像看起來(lái)像是左手坐標(biāo)系的錯(cuò)亂。正確的做法是在搭建完DH參數(shù)表后先用dh_transform畫(huà)一個(gè)單連桿的坐標(biāo)系人工檢查坐標(biāo)系指向是否和機(jī)械臂實(shí)體一致。第三坑不要在逆解函數(shù)里用inv(R)。旋轉(zhuǎn)矩陣是正交矩陣它的逆等于它的轉(zhuǎn)置。直接用transpose(R)比用inv(R)快得多而且數(shù)值穩(wěn)定性更好。這個(gè)細(xì)節(jié)在單次求解時(shí)感受不到差別但在循跡仿真幾萬(wàn)個(gè)點(diǎn)時(shí)會(huì)明顯拖慢速度。6.3 進(jìn)階能不能擴(kuò)展到運(yùn)動(dòng)學(xué)和軌跡規(guī)劃有了穩(wěn)定的正逆解求解器往上可以接的東西非常多。比如雅可比矩陣它其實(shí)是正解矩陣對(duì)關(guān)節(jié)角求偏導(dǎo)用它可以做速度層面的映射關(guān)系——末端笛卡爾速度等于雅可比矩陣乘以關(guān)節(jié)角速度。在MATLAB里可以用jacobian符號(hào)函數(shù)或者數(shù)值差分法算出雅可比進(jìn)而做奇異性分析和力的映射分析。再比如軌跡規(guī)劃笛卡爾空間的直線(xiàn)插值配合逆解就能實(shí)現(xiàn)直線(xiàn)運(yùn)動(dòng)關(guān)節(jié)空間的梯形速度規(guī)劃配合正解就能驗(yàn)證關(guān)節(jié)運(yùn)動(dòng)是否超限。所有這些都可以在這套運(yùn)動(dòng)學(xué)求解器基礎(chǔ)上擴(kuò)展不需要大改架構(gòu)。這也是我為什么堅(jiān)持用函數(shù)化、模塊化的方式來(lái)組織代碼——為了后面擴(kuò)展時(shí)不拆東墻補(bǔ)西墻。7. 總結(jié)與個(gè)人經(jīng)驗(yàn)中的關(guān)鍵心得回頭梳理一遍整個(gè)實(shí)現(xiàn)過(guò)程最讓我想提醒后來(lái)者的一件事是正逆運(yùn)動(dòng)學(xué)看著是數(shù)學(xué)題實(shí)際是工程題。很多人在MATLAB里把正逆解跑通了覺(jué)得很興奮但一旦接到真機(jī)調(diào)試就會(huì)發(fā)現(xiàn)各種實(shí)際問(wèn)題和理論不一致。關(guān)節(jié)限位、零點(diǎn)漂移、奇異位形、解跳變這些才是真正考驗(yàn)工程能力的地方。我在自己項(xiàng)目的源碼里特意保留了一套完整的注釋和測(cè)試代碼方便后續(xù)接手的人快速理解每個(gè)函數(shù)的作用和邊界條件。尤其是inverse_kinematics.m這個(gè)函數(shù)我加了幾十行注釋專(zhuān)門(mén)說(shuō)明每個(gè)解分支的來(lái)源和適用場(chǎng)景。這樣做的好處是即使過(guò)了三四個(gè)月自己回頭看代碼依然能在幾分鐘內(nèi)想起來(lái)當(dāng)初的設(shè)計(jì)意圖。另外一個(gè)特別受用的經(jīng)驗(yàn)是在做逆解驗(yàn)證時(shí)不要只測(cè)單點(diǎn)。把末端位姿連續(xù)變化、關(guān)節(jié)角連續(xù)變化、大量隨機(jī)點(diǎn)結(jié)合到一起測(cè)試才能暴露那些間歇性出現(xiàn)的穩(wěn)定性問(wèn)題。我曾經(jīng)用這套方法在模擬仿真階段就暴露了一個(gè)關(guān)節(jié)角跳變問(wèn)題省掉了后面真機(jī)調(diào)試的大量返工時(shí)間。這種把問(wèn)題提前暴露在仿真里的做法是我強(qiáng)烈推薦的工作習(xí)慣。最后分享一個(gè)小技巧當(dāng)你把逆解結(jié)果用于軌跡規(guī)劃時(shí)可以在每個(gè)目標(biāo)點(diǎn)求解之前傳入上一時(shí)刻的關(guān)節(jié)角作為參考讓逆解自動(dòng)偏向連續(xù)性更好的候選解。這個(gè)技巧看起來(lái)簡(jiǎn)單但對(duì)于整個(gè)系統(tǒng)的運(yùn)動(dòng)平穩(wěn)性提升非常明顯建議你實(shí)際體驗(yàn)一下。本文還有配套的精品資源點(diǎn)擊獲取