V-rep學習筆記:機器人逆運動學數值解法(Cyclic Coordinate Descent Method)


  When performing inverse kinematics (IK) on a complicated bone chain, it can become too complex for an analytical solution. Cyclic Coordinate Descent (CCD) is an alternative that is both easy to implement and efficient to process。逆運動學問題一般采用解析法和基於Jacobian矩陣的迭代方法,前者雖然精度高而且能達到實時的效果,但是隨着關節的增多,自由度隨着增多,數學建模也變得很困難,甚至不可解。而后者很難達到實時的效果。

CCD 算法的思想

  Cyclic Coordinate Descent (CCD) 是一個啟發式的迭代搜索算法,它通過每一次只改變一個關節的參數來逐步減少位置誤差和姿態誤差,每個迭代過程包括一個從關節鏈結構的末端到基點的遍歷過程。由於CCD 方法將多關節的關節鏈問題簡化為單關節問題,可以用解析法處理,因此每一步的迭代可以相當快。當求得每個關節的參數 (轉角)θ后,將其代入正向運動學方程求得末端效器和每個關節的位置。從運動鏈的末端開始 , 逐步改變每個關節的旋轉角度。先是改變最末端的關節,末端關節到末段執行器的向量為圖中藍色線段,末端關節到目標點的向量為圖中紅色線段。求出 2 個向量的夾角α,讓末端關節下的子鏈繞旋轉軸轉α角度,則末端執行器達到一個新位置。若沒有達到目標,則繼續取當前關節的上一關節,改變其旋轉角度,直到選到根節點。若末端還沒有達到目標位置,則又從末端關節開始新一輪運動,直到位置誤差足夠小或者到達了給定的循環次數。

  After our first loop through the bone chain, we have moved the end effector much closer to the target position. By repeating this process, we will continue to get closer and closer. Once we have reached a tolerable distance from the target position or once we have performed too many iterations (for performance reasons), we can stop looping.下面三幅圖展示了CCD算法的3次迭代過程,可以看出隨着迭代的進行,末端離目標點越來越近。

下面在V-rep中建立平面3連桿機構,各桿長均為0.5m,使用Python腳本計算運動學逆解並控制V-rep中的模型,使其達到目標位置。

# -*- coding: utf-8 -*-
import vrep             # V-rep library
import sys import time import math # This function will convert an angle to the equivalent rotation in the range [-pi,pi]
def ConfineAngle(angle): angle = angle % (2.0 * math.pi) if( angle < -math.pi ): angle += (2.0 * math.pi) if( angle > math.pi ): angle -= (2.0 * math.pi) return angle def CalcIK(): id = linkNum - 1
    while id >= 0: retcode, J_pos = vrep.simxGetObjectPosition(clientID,joint_handle[id],-1,vrep.simx_opmode_oneshot_wait) retcode, tip = vrep.simxGetObjectPosition(clientID,tip_handle, -1, vrep.simx_opmode_oneshot_wait) # Get the vector from the current bone to the end effector position.
        curToEndX = tip[0] - J_pos[0] curToEndY = tip[1] - J_pos[1] curToEndMag = math.sqrt( curToEndX*curToEndX + curToEndY*curToEndY ) # Get the vector from the current bone to the target position.
        curToTargetX = target[0] - J_pos[0]; curToTargetY = target[1] - J_pos[1]; curToTargetMag = math.sqrt(curToTargetX*curToTargetX+curToTargetY*curToTargetY) # Get rotation 
        endTargetMag = curToEndMag*curToTargetMag if endTargetMag <= 0.0001:    # prevent division by small numbers
            cosRotAng = 1 sinRotAng = 0 else: cosRotAng = (curToEndX*curToTargetX + curToEndY*curToTargetY) / endTargetMag sinRotAng = (curToEndX*curToTargetY - curToEndY*curToTargetX) / endTargetMag # Clamp the cosine into range when computing the angle(might be out of rangedue to floating point error)
        rotAng = math.acos(max(-1, min(1,cosRotAng))) if  sinRotAng < 0.0: rotAng = -rotAng q[id] = q[id] + rotAng # Rotate the current link
        if(id == 0): vrep.simxSetJointPosition(clientID,joint_handle[id], ConfineAngle(q[id])+math.pi/2, vrep.simx_opmode_oneshot) else: vrep.simxSetJointPosition(clientID,joint_handle[id], ConfineAngle(q[id]), vrep.simx_opmode_oneshot) # Check for termination
        retcode, tip = vrep.simxGetObjectPosition(clientID,tip_handle, -1, vrep.simx_opmode_oneshot_wait) endToTargetX = (target[0] - tip[0]) endToTargetY = (target[1] - tip[1]) error = math.sqrt(endToTargetX*endToTargetX + endToTargetY*endToTargetY) if( error <= stol ): # We found a valid solution.
            return 1, error id = id - 1
        
    return 0, error  # cannot get to the target in this iteration
    
    
        
if __name__ == "__main__": # Starts a communication thread with the server
    clientID = vrep.simxStart('127.0.0.1', 20001, True, True, 5000, 5) # clientID: the client ID, or -1 if the connection to the server was not possible
    if clientID != -1:  #check if client connection successful
        print 'Connected to remote API server'
    else: print 'Connection not successful' sys.exit('Could not connect')    # Exit from Python


    # Retrieves an object handle based on its name.
    errorCode,tip_handle = vrep.simxGetObjectHandle(clientID,'tip',vrep.simx_opmode_oneshot_wait) errorCode,target_handle = vrep.simxGetObjectHandle(clientID,'target',vrep.simx_opmode_oneshot_wait) errorCode,consoleHandle = vrep.simxAuxiliaryConsoleOpen(clientID,'info',4,1+4,None,None,None,None,vrep.simx_opmode_oneshot_wait) joint_handle = [-1,-1,-1]    # store the joint handles
    for i in range(3): errorCode,joint_handle[i] = vrep.simxGetObjectHandle(clientID,'j'+str(i+1),vrep.simx_opmode_oneshot_wait) ilimit = 100    # maximum iteration
    stol = 1e-2     # tolerance
    q = [0,0,0]     # initial joint value
    linkNum = 3     # number of links
 retcode, target = vrep.simxGetObjectPosition(clientID,target_handle, -1, vrep.simx_opmode_oneshot_wait) retcode, tip = vrep.simxGetObjectPosition(clientID,tip_handle, -1, vrep.simx_opmode_oneshot_wait) count = 0 isOK = 0 while ( not isOK ): isOK,err = CalcIK() vrep.simxAuxiliaryConsolePrint(clientID,consoleHandle,None,vrep.simx_opmode_oneshot_wait) count = count + 1 vrep.simxAuxiliaryConsolePrint(clientID,consoleHandle,str(count)+' iterations err:'+str(err),vrep.simx_opmode_oneshot_wait) if count > ilimit: vrep.simxAuxiliaryConsolePrint(clientID,consoleHandle,"Solution wouldn't converge\r\n",vrep.simx_opmode_oneshot_wait) break
        #time.sleep(0.1)
    
    # Ends the communication thread. This should be the very last remote API function called on the client side
    vrep.simxFinish(clientID)

  點擊仿真按鈕並運行Python控制腳本后,可以看到V-rep中的連桿模型不斷調整其關節角,同時誤差err逐漸減小。當誤差減小到一定程度,就可以停止迭代。下面三幅圖中目標處於不同位置,可以發現目標位置對迭代次數有較大的影響(為什么會這樣?)

 

 

參考:

1.Cyclic Coordinate Descent in 2D :http://www.ryanjuckett.com/programming/cyclic-coordinate-descent-in-2d/

2. 陽小濤 ,楊克儉. CCD 算法及其在逆運動學中的應用與實現[J]. 重 慶 工 學 院 學 報 (自然科學),2008 年 5 月

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM