serialRobotInverseKinematics.py

You can view and download this file on Github: serialRobotInverseKinematics.py

  1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2# This is an EXUDYN example
  3#
  4# Details:  Example of a serial robot with minimal coordinates using the inverse kinematics Funcion of Exudyn
  5#
  6# Author:   Johannes Gerstmayr
  7# Date:     2023-03-29
  8#
  9# Copyright:This file is part of Exudyn. Exudyn is free software. You can redistribute it and/or modify it under the terms of the Exudyn license. See 'LICENSE.txt' for more details.
 10#q
 11#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 12
 13
 14import exudyn as exu
 15from exudyn.itemInterface import *
 16from exudyn.utilities import * #includes itemInterface and rigidBodyUtilities
 17import exudyn.graphics as graphics #only import if it does not conflict
 18from exudyn.rigidBodyUtilities import *
 19from exudyn.graphicsDataUtilities import *
 20from exudyn.robotics import *
 21from exudyn.robotics.motion import Trajectory, ProfileConstantAcceleration, ProfilePTP
 22from exudyn.robotics.models import ManipulatorPuma560, ManipulatorPANDA, ManipulatorUR5, LinkDict2Robot
 23from exudyn.lieGroupBasics import LogSE3, ExpSE3
 24
 25import numpy as np
 26
 27exu.RequireVersion('1.6.31')
 28# exuVersion = np.array(exu.__version__.split('.')[:3], dtype=int)
 29exuVersion = exu.__version__.split('.')[:3]
 30exuVersion = exuVersion[0] + exuVersion[1] + exuVersion[2]
 31if int(exuVersion) < 1631:
 32    print('\nWarning: use at least exudyn verion 1.6.31.dev1')
 33    print('You can install the latest development version with\npip install -U exudyn --pre\n\n')
 34
 35
 36#%% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 37#motion planning:
 38jointSpaceInterpolation = False #false interpolates TCP position in work space/Cartesian coordinates
 39motionCase = 2 # case 1 and 2 move in different planes
 40
 41
 42#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 43#kinematic tree and redundant mbs agrees for stdDH version up to 1e-10, with compensateStaticTorques = False
 44# KT:      rotations at tEnd= 1.8464514676503092 , [0.4921990591981066, 0.2718999073958087, 0.818158053005264, -0.0030588904101585936, 0.26831938569719394, -0.0010660472359057434]
 45# red. MBS:rotations at tEnd= 1.8464514674961 ,   [ 0.49219906  0.27189991  0.81815805 -0.00305889  0.26831939 -0.00106605]
 46
 47#some graphics settings to get base and tool visualization
 48jointWidth=0.1
 49jointRadius=0.06
 50linkWidth=0.1
 51
 52graphicsBaseList = [graphics.Brick([0,0,-0.35], [0.12,0.12,0.5], graphics.color.grey)]
 53graphicsBaseList +=[graphics.Cylinder([0,0,0], [0.5,0,0], 0.0025, graphics.color.red)]
 54graphicsBaseList +=[graphics.Cylinder([0,0,0], [0,0.5,0], 0.0025, graphics.color.green)]
 55graphicsBaseList +=[graphics.Cylinder([0,0,0], [0,0,0.5], 0.0025, graphics.color.blue)]
 56graphicsBaseList +=[graphics.Cylinder([0,0,-jointWidth], [0,0,jointWidth], linkWidth*0.5, graphics.colorList[0])] #belongs to first body
 57graphicsBaseList +=[graphics.CheckerBoard([0,0,-0.6], [0,0,1], size=2)]
 58
 59ty = 0.03
 60tz = 0.04
 61zOff = -0.05
 62toolSize= [0.05,0.5*ty,0.06]
 63graphicsToolList = [graphics.Cylinder(pAxis=[0,0,zOff], vAxis= [0,0,tz], radius=ty*1.5, color=graphics.color.red)]
 64graphicsToolList+= [graphics.Brick([0,ty,1.5*tz+zOff], toolSize, graphics.color.grey)]
 65graphicsToolList+= [graphics.Brick([0,-ty,1.5*tz+zOff], toolSize, graphics.color.grey)]
 66
 67# here another robot could be loaded; note that the robots sizes and workspaces and zero configuration
 68# differ, so the HTmove may need to be adjusted to be inside the workspace
 69robotDef = ManipulatorPuma560()
 70# robotDef = ManipulatorUR5()
 71# robotDef = ManipulatorPANDA()
 72HTtool = HTtranslate([0,0,0.08])
 73
 74robot = Robot(gravity=[0,0,-9.81],
 75              base = RobotBase(HT=HTtranslate([0,0,0]), visualization=VRobotBase(graphicsData=graphicsBaseList)),
 76              tool = RobotTool(HT=HTtool, visualization=VRobotTool(graphicsData=graphicsToolList)),
 77              referenceConfiguration = []) #referenceConfiguration created with 0s automatically
 78
 79robot=LinkDict2Robot(robotDef, robot)
 80
 81ik = InverseKinematicsNumerical(robot=robot, useRenderer=False,
 82                                #flagDebug=True,
 83                                #jointStiffness=1e4
 84                                ) #, useAlternativeConstraints=True)
 85SC = exu.SystemContainer()
 86SC.AttachToRenderEngine()
 87mbs = SC.AddSystem()
 88# the system container holds one or several systems; the inverse kinematics uses a second system container without visualization
 89# in the mbs the mainsystem (multibody system) is stored; in the SC more than one mbs can be created but they do not interact with each other.
 90
 91
 92#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 93#configurations and trajectory
 94q0 = [0,0,0,0,0,0] #zero angle configuration
 95# q1 = [0, pi/8, pi*0.5, 0,pi/8,0] #configuration 1
 96# q2 = [0.8*pi,-0.8*pi, -pi*0.5,0.75*pi,-pi*0.4,pi*0.4] #configuration 2
 97# q3 = [0.5*pi,0,-0.25*pi,0,0,0] # configuration 3
 98
 99
100#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
101#create robot model in mbs
102#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
103
104mbs.variables['myIkine'] = ik
105jointHTs = robot.JointHT(q0)
106HTlastJoint = jointHTs[-1]@HTtool
107
108#prescribed motion:
109#HTmove = HTtranslate([-0.25,0.,0.3])
110if motionCase == 1:
111    HTmove = HT(RotationMatrixX(-0.3*pi),[-0.45,0.,0.]) #goes through singularity
112elif motionCase == 2:
113    HTmove = HT(RotationMatrixX(0.3*pi),[0.,0.,-0.3])    #no singularity
114else:
115    print('no valid motionCase provided')
116
117## here another motionCase/HTmove could be added
118
119logMove = LogSE3(HTmove)
120rotMove = Skew2Vec(logMove[0:3,0:3])
121dispMove = HT2translation(logMove)
122
123q0=[0,0,0,0.01,0.02,0.03]
124[q1, success] = ik.SolveSafe(HTlastJoint@HTmove, q0)
125if not success: print('[q1, success]=',[q1, success])
126[q2, success] = ik.SolveSafe(HTlastJoint@HTmove@HTmove, q1)
127if not success: print('[q2, success]=',[q2, success])
128
129#initialize for first simulation step:
130[q, success] = ik.Solve(HTlastJoint, q0)
131print('[q, success]=',[q, success])
132
133#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
134#trajectory generated with optimal acceleration profiles, for joint-interpolation:
135trajectory = Trajectory(initialCoordinates=q0, initialTime=0)
136trajectory.Add(ProfileConstantAcceleration(q1,1))
137trajectory.Add(ProfileConstantAcceleration(q1,1))
138trajectory.Add(ProfileConstantAcceleration(q2,1))
139trajectory.Add(ProfileConstantAcceleration(q2,1))
140#traj.Add(ProfilePTP([1,1],syncAccTimes=False, maxVelocities=[1,1], maxAccelerations=[5,5]))
141#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
142
143
144
145
146#use frame for prescribed TCP:
147gFrame = [graphics.Frame(HTlastJoint, length=0.3, colors=[graphics.color.lightgrey]*3)]
148gFrame += [graphics.Frame(HTlastJoint@HTmove, length=0.3, colors=[graphics.color.grey]*3)]
149gFrame += [graphics.Frame(HTlastJoint@HTmove@HTmove, length=0.3, colors=[graphics.color.dodgerblue]*3)]
150oGround = mbs.AddObject(ObjectGround(visualization=VObjectGround(graphicsData=gFrame)))
151
152robotDict = robot.CreateKinematicTree(mbs)
153oKT = robotDict['objectKinematicTree']
154
155#add sensor for joint coordinates (relative to reference coordinates)
156sJoints = mbs.AddSensor(SensorNode(nodeNumber=robotDict['nodeGeneric'], storeInternal=True,
157                         outputVariableType=exu.OutputVariableType.Coordinates))
158
159sPosTCP = mbs.AddSensor(SensorKinematicTree(objectNumber=oKT, linkNumber=5, localPosition=HT2translation(HTtool),
160                                            storeInternal=True,
161                                            outputVariableType=exu.OutputVariableType.Position))
162
163sRotTCP = mbs.AddSensor(SensorKinematicTree(objectNumber=oKT, linkNumber=5, localPosition=HT2translation(HTtool),
164                                            storeInternal=True,
165                                            outputVariableType=exu.OutputVariableType.RotationMatrix))
166
167#user function which is called only once per step, speeds up simulation drastically
168def PreStepUF(mbs, t):
169    if not jointSpaceInterpolation:
170        ik = mbs.variables['myIkine']
171        if t < 2:
172            tt = min(t,1)
173            T = HTlastJoint@ExpSE3(list(tt*dispMove)+list(tt*rotMove))
174        elif t < 4:
175            tt=min(t-2,1)
176            T = HTlastJoint@HTmove@ExpSE3(list(tt*dispMove)+list(tt*rotMove))
177        else:
178            T = HTlastJoint@HTmove@HTmove
179
180        [q,success]=ik.Solve(T) #, q0) #takes 60 us internally
181        mbs.SetObjectParameter(oKT, 'jointPositionOffsetVector', q)
182
183    else:
184        #standard trajectory planning:
185
186        [u,v,a] = trajectory.Evaluate(t)
187        #in case of kinematic tree, very simple operations!
188        mbs.SetObjectParameter(oKT, 'jointPositionOffsetVector', u)
189        mbs.SetObjectParameter(oKT, 'jointVelocityOffsetVector', v)
190
191    return True
192
193mbs.SetPreStepUserFunction(PreStepUF)
194
195
196
197
198mbs.Assemble() # assemble the dynamic system
199#mbs.systemData.Info()
200
201#%% setting for visualization
202SC.visualizationSettings.connectors.showJointAxes = True
203SC.visualizationSettings.connectors.jointAxesLength = 0.02
204SC.visualizationSettings.connectors.jointAxesRadius = 0.002
205SC.visualizationSettings.nodes.showBasis = True
206SC.visualizationSettings.nodes.basisSize = 0.1
207SC.visualizationSettings.loads.show = False # shows external loads
208SC.visualizationSettings.bodies.kinematicTree.showJointFrames = False # shows the frames for each joint of the robot
209SC.visualizationSettings.openGL.multiSampling=4
210SC.visualizationSettings.general.autoFitScene=False
211SC.visualizationSettings.window.renderWindowSize=[1920,1200]
212SC.visualizationSettings.general.graphicsUpdateInterval = 0.01
213SC.visualizationSettings.openGL.shadow=0.3
214SC.visualizationSettings.openGL.perspective=0.5
215
216#traces:
217SC.visualizationSettings.sensors.traces.listOfPositionSensors = [sPosTCP]
218SC.visualizationSettings.sensors.traces.listOfTriadSensors =[sRotTCP]
219SC.visualizationSettings.sensors.traces.showPositionTrace=True
220SC.visualizationSettings.sensors.traces.showTriads=True
221SC.visualizationSettings.sensors.traces.showVectors=False
222SC.visualizationSettings.sensors.traces.showFuture=False
223SC.visualizationSettings.sensors.traces.triadsShowEvery=5
224
225#%%
226tEnd = 5 # endtime of the simulation
227h = 0.002 #500 steps take 0.16 seconds, 0.3ms / step (83% Python + inverse kinematics)
228
229# settings for the time integration / simulation
230simulationSettings = exu.SimulationSettings() #takes currently set values or default values
231simulationSettings.timeIntegration.numberOfSteps = int(tEnd/h)
232simulationSettings.timeIntegration.endTime = tEnd
233simulationSettings.solutionSettings.solutionWritePeriod = 0.02
234# determines the timesteps in which the solution is saved; when it is decreased more solutions are saved, which also
235# enables the solutionViewer to
236
237simulationSettings.solutionSettings.sensorsWritePeriod = 0.005
238#simulationSettings.solutionSettings.writeSolutionToFile = False
239# simulationSettings.timeIntegration.simulateInRealtime = True
240# simulationSettings.timeIntegration.realtimeFactor = 0.025 # slow down simulation to look at
241
242simulationSettings.timeIntegration.verboseMode = 1
243simulationSettings.displayComputationTime = True
244# simulationSettings.displayStatistics = True
245#simulationSettings.linearSolverType = exu.LinearSolverType.EigenSparse
246simulationSettings.timeIntegration.newton.useModifiedNewton = True
247
248# solver type
249simulationSettings.timeIntegration.generalizedAlpha.computeInitialAccelerations=True
250
251#%%
252useGraphics = True
253# if useGraphics is false rendering while calculating the solution is dectivated,
254# the solution is still saved and can be then visualized afterwards for the solution viewer
255
256
257if useGraphics:
258    # start graphics
259    exu.StartRenderer()
260    if 'renderState' in exu.sys:
261        SC.SetRenderState(exu.sys['renderState'])
262    mbs.WaitForUserToContinue()
263
264# hte the simulation with the set up simulationsettings is started
265mbs.SolveDynamic(simulationSettings,
266                 #solverType = exudyn.DynamicSolverType.TrapezoidalIndex2, # different solver types can be used depending on the problem
267                 showHints=True)
268
269
270if useGraphics: # when graphics are deactivated the renderer does not need to be stopped
271    SC.visualizationSettings.general.autoFitScene = False
272    exu.StopRenderer()
273
274#%%++++++++++++++++++++++++++++++++++++++++++++
275if True:
276    # import the solution viewer which loads
277
278    mbs.SolutionViewer()
279#%%++++++++++++++++++++++++++++++++++++++++++++
280
281
282q = mbs.GetObjectOutputBody(oKT, exu.OutputVariableType.Coordinates)
283exu.Print("rotations at tEnd=", VSum(q), ',', q)
284
285#%%++++++++++++++++++++++++++++++++++++++++++++
286if True:
287
288    labels = []
289    for i in range(6):
290        labels += ['joint '+str(i)]
291    # plot data from the sensor sJoints 6 times and take the values 0 to 5 in the steps
292    mbs.PlotSensor(sensorNumbers=[sJoints]*6, components=list(np.arange(6)), yLabel='joint coordinates', labels=labels)