雙足機器人簡單步態生成


   讓機器人行走最簡單的方法是先得到一組步態曲線,即腿部每個關節隨時間運動的角度值。可以在ADAMS或3D Max、Blender等軟件中建立好機構/骨骼模型,設計出腳踝和髖關節的運動曲線,然后進行逆運動學解算,測量每個關節在運動過程中的轉角,最后將得到的曲線導出。拿到曲線數據后我們就可以用單片機讀取,然后發送給機器人的舵機去執行運行。這種方法的缺點是機器人只能按照固定的步態行走,不夠靈活,比如抬腳高度、步長等參數都是定死的,如果需要修改還得再使用別的軟件導出新的步態數據。

  最簡單的腿部結構如下圖所示,在髖關節、膝關節和踝關節各有一個轉動自由度,可以通過三角形余弦定理求得機構的運動學逆解。這種機器人只能在矢狀面上直線前進,不能轉彎。對行走過程進行一定的簡化和假設:

1. 髖關節始終保持恆定的高度(實際上會有微小的波動)

2. 機器人腳面始終平行於地面

  為了確定每個關節的角度,需要設計行走過程中踝關節點的運動軌跡。這里采用簡單的正弦曲線作為其軌跡(也可以采用樣條曲線、Bézier 曲線等),正弦曲線的幅值對應抬腳最大高度。

 

  在Python中實現導入雙腿模型文件,生成指定的步態數據后讓其循環運動,就可以模擬機器人行走。代碼如下(很糙...只是實現了基本功能,細節還有待完善)

#!/usr/bin/env python
import vtk
import math
from vtk.util.colors import *
import numpy as np
import time


actor  = list()   # the list of links
filenames = ["link-1.stl","link-2.stl","link-3.stl","link-4.stl","link-5.stl","link-6.stl"]
renWin = vtk.vtkRenderWindow()

joint1 = vtk.vtkAssembly()
joint2 = vtk.vtkAssembly()
joint3 = vtk.vtkAssembly()
joint4 = vtk.vtkAssembly()
joint5 = vtk.vtkAssembly()
joint6 = vtk.vtkAssembly()

ThighLength = 100.0
ShankLength = 100.0
HipHeight = 180.0
FootLift = 10
StrideLength = 60
Subdivision = 20

leg_joint = np.zeros(3, dtype=np.float)
patterns = np.zeros((2*Subdivision ,6), dtype=np.float)
_p = 0

txt = vtk.vtkTextActor()
distance = 0.0


def Rad2Deg(rad):
    return  rad * 180.0 / math.pi


def FootHeight(x): 
    return (HipHeight - FootLift * math.cos(abs(x * math.pi / StrideLength)))


def LegIK(x, y):
    global leg_joint
    dist = math.sqrt(x**2 + y**2)
    leg_joint[0] = math.acos(dist / (2 * ShankLength)) + math.atan2(x, y)
    leg_joint[1] = math.pi - math.acos((ThighLength**2 + ShankLength**2 - dist**2) / (2 * ThighLength* ShankLength))
    leg_joint[2] = leg_joint[1] - leg_joint[0]


def GenerateGait():
    global leg_joint
    global patterns

    # Move left leg forward.  
    for i in range(Subdivision):
        x = (i - Subdivision/2) * (StrideLength / Subdivision)
        y = FootHeight(x)        
        LegIK(x, y)            
        patterns[i, :3] = Rad2Deg(leg_joint)

    # Move left leg backward.  
    for i in range(Subdivision):
        x = (Subdivision/2 - i) * (StrideLength / Subdivision)
        y = HipHeight
        LegIK(x, y)                
        patterns[i+Subdivision, :3] = Rad2Deg(leg_joint)  

    # Build right leg from phase shift clone of left. 
    for i in range(2*Subdivision):
        patterns[i, 3:] = -patterns[(i + Subdivision) % (2*Subdivision), :3]


 
# Customize vtkInteractorStyleTrackballCamera 
class MyInteractor(vtk.vtkInteractorStyleTrackballCamera):
    def __init__(self,parent=None):
        self.AddObserver("CharEvent",self.OnCharEvent)
        self.AddObserver("KeyPressEvent",self.OnKeyPressEvent)

    def OnCharEvent(self,obj,event):
        pass
    
    def OnKeyPressEvent(self,obj,event):
        global _p
        global distance
        
        # Get the compound key strokes for the event
        key = self.GetInteractor().GetKeySym()
        
        GenerateGait()

        if(key == "Return"):
            # start animation
            joint1.SetPosition(0, 0, HipHeight-ThighLength-ShankLength)
            joint4.SetPosition(0, 0, HipHeight-ThighLength-ShankLength)
            
            if (_p == 2*Subdivision):
                _p = 0
     
            joint1.SetOrientation(0, -patterns[_p][0], 0)
            joint2.SetOrientation(0, patterns[_p][1], 0)
            joint3.SetOrientation(0, -patterns[_p][2], 0)

            joint4.SetOrientation(0, patterns[_p][3], 0)
            joint5.SetOrientation(0, -patterns[_p][4], 0)
            joint6.SetOrientation(0, patterns[_p][5], 0)

            _p = _p + 1

            distance = distance + StrideLength/(2 * Subdivision * 1000.0)
            txt.SetInput("Distance: " + str(distance) + "m")
            
            renWin.Render()
                
        return
    


def CreateCoordinates():
    # create coordinate axes in the render window
    axes = vtk.vtkAxesActor() 
    axes.SetTotalLength(40, 40, 40)  # Set the total length of the axes in 3 dimensions 

    # Set the type of the shaft to a cylinder:0, line:1, or user defined geometry. 
    axes.SetShaftType(0) 

    transform = vtk.vtkTransform() 
    transform.Translate(0.0, 0.0, 200.0)
    axes.SetUserTransform(transform)

    axes.SetCylinderRadius(0.02) 
    axes.GetXAxisCaptionActor2D().SetWidth(0.03) 
    axes.GetYAxisCaptionActor2D().SetWidth(0.03) 
    axes.GetZAxisCaptionActor2D().SetWidth(0.03) 
    return axes


def CreateGround():
    # create plane source
    plane = vtk.vtkPlaneSource()
    plane.SetXResolution(20)
    plane.SetYResolution(20)
    plane.SetCenter(0,0,0)
    plane.SetNormal(0,0,1)

    # mapper
    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputConnection(plane.GetOutputPort())

    # actor
    actor = vtk.vtkActor()
    actor.SetMapper(mapper)
    actor.GetProperty().SetRepresentationToWireframe()
    actor.GetProperty().SetColor(light_grey)

    transform = vtk.vtkTransform()
    transform.Scale(400,400,1)
    actor.SetUserTransform(transform)

    return actor
    

def LoadSTL(filename):
    reader = vtk.vtkSTLReader()
    reader.SetFileName(filename)
    mapper = vtk.vtkPolyDataMapper() # maps polygonal data to graphics primitives
    mapper.SetInputConnection(reader.GetOutputPort())
    actor = vtk.vtkLODActor() 
    actor.SetMapper(mapper)
    return actor   # represents an entity in a rendered scene

            
def CreateScene():
    # Create a rendering window and renderer
    ren = vtk.vtkRenderer()
    renWin.AddRenderer(ren)
     
    # Create a renderwindowinteractor
    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)
    style = MyInteractor()
    style.SetDefaultRenderer(ren)
    iren.SetInteractorStyle(style)
    
    for id, file in enumerate(filenames):
        actor.append(LoadSTL(file))
        r = vtk.vtkMath.Random(.4, 1.0)
        g = vtk.vtkMath.Random(.4, 1.0)
        b = vtk.vtkMath.Random(.4, 1.0)
        actor[id].GetProperty().SetDiffuseColor(r, g, b)
        actor[id].GetProperty().SetDiffuse(.8)
        actor[id].GetProperty().SetSpecular(.5)
        actor[id].GetProperty().SetSpecularColor(1.0,1.0,1.0)
        actor[id].GetProperty().SetSpecularPower(30.0)

    joint1.AddPart(actor[0])
    joint1.AddPart(joint2)
    joint2.AddPart(actor[1])
    joint2.AddPart(joint3)
    joint3.AddPart(actor[2])

    joint4.AddPart(actor[3])
    joint4.AddPart(joint5)
    joint5.AddPart(actor[4])
    joint5.AddPart(joint6)
    joint6.AddPart(actor[5])
    
    joint1.SetOrigin(0, 0, 200)
    joint4.SetOrigin(0, 0, 200)
    joint2.SetOrigin(0, 0, 100)
    joint5.SetOrigin(0, 0, 100)

    ren.AddActor(joint1)
    ren.AddActor(joint4)

    # Add coordinates
    axes = CreateCoordinates()
    ren.AddActor(axes)

    # Add ground
    ground = CreateGround()
    ren.AddActor(ground)

    # create a text actor
    txt.SetInput("Distance: 0m")
    txtprop=txt.GetTextProperty()
    txtprop.SetFontFamilyToArial()
    txtprop.SetFontSize(18)
    txtprop.SetColor(1,1,1)
    txt.SetDisplayPosition(450,550)
        
    # assign actor to the renderer
    ren.AddActor(txt)

    # Set background color
    ren.SetBackground(.1, .1, .1)

    # Set window size
    renWin.SetSize(600, 600)

    # Enable user interface interactor
    iren.Initialize()
    iren.Start()
    
    
if __name__ == "__main__":
    CreateScene()
View Code

  按住回車鍵,一幀一幀播放動畫。最后的效果是這樣的:

 

 

參考:

https://github.com/Rhoban/IKWalk

Using Inverse Kinematics to Develop a Biped Robot Walking Gait C#

8 DOF Biped Robot using Dynamixel AX-12A Servos and Arduino


免責聲明!

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



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