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)