serialRobotTest.py

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

  1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2# This is an EXUDYN example
  3#
  4# Details:  Test Example of a serial robot with redundant coordinates
  5#           NOTE: more efficient version in serialRobotTSD.py and serialRobotKinematicTree.py using TorsionalSpringDamper
  6#
  7# Author:   Johannes Gerstmayr
  8# Date:     2020-02-16
  9# Revised:  2021-07-09
 10#
 11# 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.
 12#
 13#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 14
 15import exudyn as exu
 16from exudyn.utilities import *
 17from exudyn.rigidBodyUtilities import *
 18from exudyn.robotics import *
 19from exudyn.robotics.motion import Trajectory, ProfileConstantAcceleration
 20
 21import numpy as np
 22from numpy import linalg as LA
 23
 24useGraphics = True #without test
 25#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 26#you can erase the following lines and all exudynTestGlobals related operations if this is not intended to be used as TestModel:
 27try: #only if called from test suite
 28    from modelUnitTests import exudynTestGlobals #for globally storing test results
 29    useGraphics = exudynTestGlobals.useGraphics
 30except:
 31    class ExudynTestGlobals:
 32        pass
 33    exudynTestGlobals = ExudynTestGlobals()
 34#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 35
 36SC = exu.SystemContainer()
 37mbs = SC.AddSystem()
 38
 39#useGraphics = False
 40if useGraphics:
 41    sensorWriteToFile = True
 42else:
 43    sensorWriteToFile = False
 44
 45jointWidth=0.1
 46jointRadius=0.06
 47linkWidth=0.1
 48
 49graphicsBaseList = [GraphicsDataOrthoCubePoint([0,0,-0.15], [0.4,0.4,0.1], color4grey)]
 50graphicsBaseList +=[GraphicsDataCylinder([0,0,0], [0.5,0,0], 0.0025, color4red)]
 51graphicsBaseList +=[GraphicsDataCylinder([0,0,0], [0,0.5,0], 0.0025, color4green)]
 52graphicsBaseList +=[GraphicsDataCylinder([0,0,0], [0,0,0.5], 0.0025, color4blue)]
 53graphicsBaseList +=[GraphicsDataCylinder([0,0,-jointWidth], [0,0,jointWidth], linkWidth*0.5, color4list[0])] #belongs to first body
 54
 55ty = 0.03
 56tz = 0.04
 57zOff = -0.05
 58toolSize= [0.05,0.5*ty,0.06]
 59graphicsToolList = [GraphicsDataCylinder(pAxis=[0,0,zOff], vAxis= [0,0,tz], radius=ty*1.5, color=color4red)]
 60graphicsToolList+= [GraphicsDataOrthoCubePoint([0,ty,1.5*tz+zOff], toolSize, color4grey)]
 61graphicsToolList+= [GraphicsDataOrthoCubePoint([0,-ty,1.5*tz+zOff], toolSize, color4grey)]
 62
 63
 64#changed to new robot structure July 2021:
 65newRobot = Robot(gravity=[0,0,9.81],
 66                 base = RobotBase(visualization=VRobotBase(graphicsData=graphicsBaseList)),
 67                 tool = RobotTool(HT=HTtranslate([0,0,0.1]), visualization=VRobotTool(graphicsData=graphicsToolList)),
 68                 referenceConfiguration = []) #referenceConfiguration created with 0s automatically
 69
 70newRobot.AddLink(RobotLink(mass=20, COM=[0,0,0], inertia=np.diag([1e-8,0.35,1e-8]), localHT = StdDH2HT([0,0,0,np.pi/2]), visualization=VRobotLink(linkColor=color4list[0])))
 71newRobot.AddLink(RobotLink(mass=17.4, COM=[-0.3638, 0.006, 0.2275], inertia=np.diag([0.13,0.524,0.539]), localHT = StdDH2HT([0,0,0.4318,0]), visualization=VRobotLink(linkColor=color4list[1])))
 72newRobot.AddLink(RobotLink(mass=4.8, COM=[-0.0203,-0.0141,0.07], inertia=np.diag([0.066,0.086,0.0125]), localHT = StdDH2HT([0,0.15,0.0203,-np.pi/2]), visualization=VRobotLink(linkColor=color4list[2])))
 73newRobot.AddLink(RobotLink(mass=0.82, COM=[0,0.019,0], inertia=np.diag([0.0018,0.0013,0.0018]), localHT = StdDH2HT([0,0.4318,0,np.pi/2]), visualization=VRobotLink(linkColor=color4list[3])))
 74newRobot.AddLink(RobotLink(mass=0.34, COM=[0,0,0], inertia=np.diag([0.0003,0.0004,0.0003]), localHT = StdDH2HT([0,0,0,-np.pi/2]), visualization=VRobotLink(linkColor=color4list[4])))
 75newRobot.AddLink(RobotLink(mass=0.09, COM=[0,0,0.032], inertia=np.diag([0.00015,0.00015,4e-5]), localHT = StdDH2HT([0,0,0,0]), visualization=VRobotLink(linkColor=color4list[5])))
 76
 77
 78# newRobot = Robot(gravity=[0,0,-9.81], referenceConfiguration = [])
 79# newRobot.BuildFromDictionary(myRobot) #this allows conversion from old structure
 80
 81#assumption, as the bodies in the mbs have their COM at the reference position
 82#for link in robot['links']:
 83#    link['COM']=[0,0,0]
 84
 85#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 86#configurations and trajectory
 87q0 = [0,0,0,0,0,0] #zero angle configuration
 88
 89q1 = [0,       np.pi/8, np.pi*0.25, 0,np.pi/8,0] #configuration 1
 90q2 = [np.pi/2,-np.pi/8,-np.pi*0.125, 0,np.pi/4,0] #configuration 2
 91#q2 = [np.pi*0.45,-np.pi*0.35,-np.pi*0.25, np.pi*0.10,np.pi*0.2,np.pi*0.3] #configuration 2
 92#q2 = [pi/2,-pi/8,-pi*0.125,0,pi/4,pi/2] #configuration 2
 93
 94trajectory=Trajectory(q0, 0)
 95if False: #tests for static torque compensation
 96    #trajectory.Add(ProfileConstantAcceleration(q0, 1)) #V1.2.37: torques at tEnd= 0.3773259482700045
 97    trajectory.Add(ProfileConstantAcceleration(q2, 0.25))
 98    trajectory.Add(ProfileConstantAcceleration(q2, 0.75))
 99else: #standard test case:
100    trajectory.Add(ProfileConstantAcceleration(q1, 0.25))
101    trajectory.Add(ProfileConstantAcceleration(q2, 0.25))
102    trajectory.Add(ProfileConstantAcceleration(q0, 0.5 ))
103    trajectory.Add(ProfileConstantAcceleration(q0, 1e6))
104
105#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
106#test robot model
107#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
108#control parameters, per joint:
109Pcontrol = np.array([40000, 40000, 40000, 100, 100, 10])
110Dcontrol = np.array([400,   400,   100,   1,   1,   0.05])  #last value 0.05 gives less oscillations as compared to earlier values of 0.1!
111# Pcontrol = 0.01*Pcontrol #soft behavior
112
113
114#++++++++++++++++++++++++++++++++++++++++++++++++
115#base, graphics, object and marker:
116graphicsBaseList = [GraphicsDataOrthoCubePoint([0,0,-0.2], [0.4,0.4,0.1], color4grey)]
117graphicsBaseList +=[GraphicsDataCylinder([0,0,0], [0.5,0,0], 0.0025, color4red)]
118graphicsBaseList +=[GraphicsDataCylinder([0,0,0], [0,0.5,0], 0.0025, color4green)]
119graphicsBaseList +=[GraphicsDataCylinder([0,0,0], [0,0,0.5], 0.0025, color4blue)]
120#oGround = mbs.AddObject(ObjectGround(referencePosition=list(HT2translation(Tcurrent)),
121objectGround = mbs.AddObject(ObjectGround(referencePosition=HT2translation(newRobot.GetBaseHT()),
122                                     visualization=VObjectGround(graphicsData=graphicsBaseList)))
123
124#baseMarker; could also be a moving base!
125baseMarker = mbs.AddMarker(MarkerBodyRigid(bodyNumber=objectGround, localPosition=[0,0,0]))
126
127
128
129#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
130#build mbs robot model:
131robotDict = newRobot.CreateRedundantCoordinateMBS(mbs, baseMarker=baseMarker)
132
133#   !!!!!IMPORTANT!!!!!:
134jointList = robotDict['jointList'] #must be stored there for the load user function
135
136unitTorques0 = robotDict['unitTorque0List'] #(left body)
137unitTorques1 = robotDict['unitTorque1List'] #(right body)
138
139loadList0 = robotDict['jointTorque0List'] #(left body)
140loadList1 = robotDict['jointTorque1List'] #(right body)
141#print(loadList0, loadList1)
142#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
143#control robot
144compensateStaticTorques = True
145def ComputeMBSstaticRobotTorques(newRobot):
146    q=[]
147    for joint in jointList:
148        q += [mbs.GetObjectOutput(joint, exu.OutputVariableType.Rotation)[2]] #z-rotation
149    HT=newRobot.JointHT(q)
150    return newRobot.StaticTorques(HT)
151
152#user function which is called only once per step, speeds up simulation drastically
153def PreStepUF(mbs, t):
154    if compensateStaticTorques:
155        staticTorques = ComputeMBSstaticRobotTorques(newRobot)
156    else:
157        staticTorques = np.zeros(len(jointList))
158    #compute load for joint number
159    for i in range(len(jointList)):
160        joint = i
161        phi = mbs.GetObjectOutput(jointList[joint], exu.OutputVariableType.Rotation)[2] #z-rotation
162        omega = mbs.GetObjectOutput(jointList[joint], exu.OutputVariableType.AngularVelocityLocal)[2] #z-angular velocity
163
164        #[u,v,a] = MotionInterpolator(t, robotTrajectory, joint) #OLD, until V1.1.135
165        [u,v,a] = trajectory.EvaluateCoordinate(t, joint)
166
167
168        torque = -(Pcontrol[joint]*(phi-u) + Dcontrol[joint]*(omega-v)) #negative sign in feedback control!
169        # torque = (Pcontrol[joint]*(phi+u) + Dcontrol[joint]*(omega+v)) #until V1.2.; but static Torque compensation is wrong in this case!
170        torque -= staticTorques[joint] #add static torque compensation
171
172        load0 = torque * unitTorques0[i] #includes sign and correct unit-torque vector
173        load1 = torque * unitTorques1[i] #includes sign and correct unit-torque vector
174
175    #     #write updated torque to joint loads, applied to left and right body
176        mbs.SetLoadParameter(loadList0[i], 'loadVector', list(load0))
177        mbs.SetLoadParameter(loadList1[i], 'loadVector', list(load1))
178
179    return True
180
181mbs.SetPreStepUserFunction(PreStepUF)
182
183
184
185#add sensors:
186sJointRot = []
187sJointAngVel = []
188sJointTorque = []
189cnt = 0
190for jointLink in jointList:
191    sJointRot += [mbs.AddSensor(SensorObject(objectNumber=jointLink,
192                               storeInternal=True,#fileName="solution/joint" + str(cnt) + "Rot.txt",
193                               outputVariableType=exu.OutputVariableType.Rotation,
194                               writeToFile = sensorWriteToFile))]
195    sJointAngVel += [mbs.AddSensor(SensorObject(objectNumber=jointLink,
196                               storeInternal=True,#fileName="solution/joint" + str(cnt) + "AngVel.txt",
197                               outputVariableType=exu.OutputVariableType.AngularVelocityLocal,
198                               writeToFile = sensorWriteToFile))]
199    cnt+=1
200
201cnt = 0
202for load0 in robotDict['jointTorque0List']:
203    sJointTorque += [mbs.AddSensor(SensorLoad(loadNumber=load0,storeInternal=True,# fileName="solution/jointTorque" + str(cnt) + ".txt",
204                                              writeToFile = sensorWriteToFile))]
205    cnt+=1
206
207
208
209mbs.Assemble()
210#mbs.systemData.Info()
211
212SC.visualizationSettings.connectors.showJointAxes = True
213SC.visualizationSettings.connectors.jointAxesLength = 0.02
214SC.visualizationSettings.connectors.jointAxesRadius = 0.002
215
216SC.visualizationSettings.nodes.showBasis = True
217SC.visualizationSettings.nodes.basisSize = 0.1
218SC.visualizationSettings.loads.show = False
219
220SC.visualizationSettings.openGL.multiSampling=4
221
222tEnd = 0.2 #0.2 for testing
223h = 0.001
224
225if useGraphics:
226    tEnd = 0.2
227    #tEnd = 1 #shows exactly static torques ComputeMBSstaticRobotTorques(newRobot) and desired angles (q2) at end
228
229#mbs.WaitForUserToContinue()
230simulationSettings = exu.SimulationSettings() #takes currently set values or default values
231
232simulationSettings.timeIntegration.numberOfSteps = int(tEnd/h)
233simulationSettings.timeIntegration.endTime = tEnd
234simulationSettings.solutionSettings.solutionWritePeriod = h
235simulationSettings.solutionSettings.sensorsWritePeriod = h
236simulationSettings.solutionSettings.writeSolutionToFile = useGraphics
237# simulationSettings.timeIntegration.simulateInRealtime = True
238# simulationSettings.timeIntegration.realtimeFactor = 0.25
239
240simulationSettings.timeIntegration.verboseMode = 1
241simulationSettings.displayComputationTime = False
242simulationSettings.displayStatistics = False
243simulationSettings.linearSolverType = exu.LinearSolverType.EigenSparse
244
245#simulationSettings.timeIntegration.newton.useModifiedNewton = True
246simulationSettings.timeIntegration.generalizedAlpha.useIndex2Constraints = False
247simulationSettings.timeIntegration.generalizedAlpha.useNewmark = simulationSettings.timeIntegration.generalizedAlpha.useIndex2Constraints
248simulationSettings.timeIntegration.generalizedAlpha.spectralRadius = 0.5 #0.6 works well
249
250simulationSettings.timeIntegration.generalizedAlpha.computeInitialAccelerations=True
251
252mbs.SolveDynamic(simulationSettings)
253
254if useGraphics:
255    SC.visualizationSettings.general.autoFitScene=False
256    exu.StartRenderer()
257    if 'renderState' in exu.sys:
258        SC.SetRenderState(exu.sys['renderState'])
259
260
261    mbs.SolutionViewer()
262    exu.StopRenderer()
263
264lastRenderState = SC.GetRenderState() #store model view
265
266#compute final torques:
267measuredTorques=[]
268
269for cnt, sensorNumber in enumerate(sJointTorque):
270    if useGraphics:
271        exu.Print('sensor torque',cnt, '=', mbs.GetSensorValues(sensorNumber))
272    measuredTorques += [1e-2*mbs.GetSensorValues(sensorNumber)[2]]
273
274if useGraphics:
275    for cnt, sensorNumber in enumerate(sJointRot):
276        exu.Print('sensor rot ',cnt, '=', mbs.GetSensorValues(sensorNumber))
277
278
279
280exu.Print("torques at tEnd=", VSum(measuredTorques))
281
282#add larger test tolerance for 32/64bits difference
283exudynTestGlobals.testError = (VSum(measuredTorques) - 0.7681856909852399)  #until 2022-04-21: 7680031232063571; until 2021-09-10: 76.8003123206452; until 2021-08-19 (changed robotics.py): 76.80031232091771; old controller: 77.12176106978085) #OLDER results: up to 2021-06-28: 0.7712176106955341; 2020-08-25: 77.13193176752571 (32bits),   2020-08-24: (64bits)77.13193176846507
284exudynTestGlobals.testResult = VSum(measuredTorques)
285
286#exu.Print('error=', exudynTestGlobals.testError)
287
288if useGraphics:
289
290
291    mbs.PlotSensor(sJointTorque, components=2, closeAll=True, yLabel='joint torques (Nm)', title='joint torques')
292
293    mbs.PlotSensor(sJointRot, components=2, yLabel='joint angles (rad)', title='joint angles')
294
295    #V1.2.40, P39: with D[-1]=0.05: since 2022-04-22:
296    # sensor torque 0 = [  0.           0.         -12.68901871]
297    # sensor torque 1 = [-0.         -0.         76.45031947]
298    # sensor torque 2 = [-0.         -0.         12.97010176]
299    # sensor torque 3 = [ 0.00000000e+00  0.00000000e+00 -8.56924875e-05]
300    # sensor torque 4 = [-0.         -0.          0.08725323]
301    # sensor torque 5 = [ 0.0000000e+00  0.0000000e+00 -9.5769297e-07]
302    # sensor rot  0 = [-1.04899069e-15  3.05694259e-19 -3.19543456e-04]
303    # sensor rot  1 = [-4.57411886e-14 -1.77599905e-14  3.63829923e-01]
304    # sensor rot  2 = [ 4.68252006e-14 -1.78150803e-14  7.25569701e-01]
305    # sensor rot  3 = [-1.07990283e-13  1.95010674e-13  2.42757634e-07]
306    # sensor rot  4 = [ 1.07852866e-13 -2.06184741e-13  3.63658987e-01]
307    # sensor rot  5 = [ 1.84235149e-16 -6.26054764e-13 -5.07549126e-08]
308    # torques at tEnd= 0.7681856909852399
309
310    #V1.2.40, P39: with D[-1]=0.1:
311    # sensor torque 0 = [  0.           0.         -12.68901812]
312    # sensor torque 1 = [-0.        -0.        76.4503195]
313    # sensor torque 2 = [-0.         -0.         12.97010177]
314    # sensor torque 3 = [ 0.0000000e+00  0.0000000e+00 -8.9097007e-05]
315    # sensor torque 4 = [-0.         -0.          0.08725323]
316    # sensor torque 5 = [-0.00000000e+00 -0.00000000e+00  1.09612342e-05]
317    # sensor rot  0 = [-1.04899069e-15  3.32799313e-19 -3.19543454e-04]
318    # sensor rot  1 = [-4.59632332e-14 -1.77794520e-14  3.63829923e-01]
319    # sensor rot  2 = [ 4.71643413e-14 -1.78734434e-14  7.25569701e-01]
320    # sensor rot  3 = [-1.07609037e-13  1.95343741e-13  2.42965135e-07]
321    # sensor rot  4 = [ 1.07471637e-13 -2.06279100e-13  3.63658987e-01]
322    # sensor rot  5 = [ 1.84106879e-16 -6.25874352e-13  8.28833899e-08]
323    # torques at tEnd= 0.7681857824086907
324
325    #V1.2.37, P37: ==> uses wrong static torque compensation
326    # sensor torque 0 = [  0.           0.         -12.67938332]
327    # sensor torque 1 = [-0.         -0.         76.42613004]
328    # sensor torque 2 = [-0.         -0.         12.96641046]
329    # sensor torque 3 = [ 0.00000000e+00  0.00000000e+00 -8.93114211e-05]
330    # sensor torque 4 = [-0.         -0.          0.08723331]
331    # sensor torque 5 = [-0.0000000e+00 -0.0000000e+00  1.1143982e-05]
332    # sensor rot  0 = [ 1.60812265e-16 -2.71050543e-20  3.19257214e-04]
333    # sensor rot  1 = [ 5.06049354e-14 -1.91882065e-14 -3.63432530e-01]
334    # sensor rot  2 = [-4.07000942e-14  3.58791681e-14 -7.25182770e-01]
335    # sensor rot  3 = [ 9.91554982e-13  4.63740157e-13 -2.41708605e-07]
336    # sensor rot  4 = [-9.26617234e-13  3.52539928e-13 -3.63103888e-01]
337    # sensor rot  5 = [ 9.21913969e-18  3.74700271e-16 -8.34127775e-08]
338    # torques at tEnd= 0.7680031232065901