mecanumWheelRollingDiscTest.py
You can view and download this file on Github: mecanumWheelRollingDiscTest.py
1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2# This is an EXUDYN example
3#
4# Details: mecanum wheels modeled by ObjectConnectorRollingDiscPenalty
5# specific friction angle of rolling disc is used to model rolls of mecanum wheels
6# formulation is still under development and needs more testing
7#
8# Author: Johannes Gerstmayr
9# Date: 2020-06-19
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 * #includes itemInterface and rigidBodyUtilities
17import exudyn.graphics as graphics #only import if it does not conflict
18
19import numpy as np
20
21useGraphics = True #without test
22#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23#you can erase the following lines and all exudynTestGlobals related operations if this is not intended to be used as TestModel:
24try: #only if called from test suite
25 from modelUnitTests import exudynTestGlobals #for globally storing test results
26 useGraphics = exudynTestGlobals.useGraphics
27except:
28 class ExudynTestGlobals:
29 pass
30 exudynTestGlobals = ExudynTestGlobals()
31#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32
33SC = exu.SystemContainer()
34mbs = SC.AddSystem()
35
36g = [0,0,-9.81] #gravity in m/s^2
37
38doBreaking = False
39
40#++++++++++++++++++++++++++++++
41#wheel parameters:
42rhoWheel = 500 #density kg/m^3
43rWheel = 0.4 #radius of disc in m
44wWheel = 0.2 #width of disc in m, just for drawing
45p0Wheel = [0,0,rWheel] #origin of disc center point at reference, such that initial contact point is at [0,0,0]
46initialRotationCar = RotationMatrixZ(0)
47
48v0 = -5*0 #initial car velocity in y-direction
49omega0Wheel = [v0/rWheel,0,0] #initial angular velocity around z-axis
50
51#v0 = [0,0,0] #initial translational velocity
52#exu.Print("v0Car=",v0)
53
54#++++++++++++++++++++++++++++++
55#car parameters:
56p0Car = [0,0,rWheel] #origin of disc center point at reference, such that initial contact point is at [0,0,0]
57lCar = 3 #y-direction
58wCar = 3 #x-direction
59hCar = rWheel #z-direction
60mCar = 500
61omega0Car = [0,0,0] #initial angular velocity around z-axis
62v0Car = [0,-v0,0] #initial velocity of car center point
63
64#inertia for infinitely small ring:
65inertiaWheel = InertiaCylinder(density=rhoWheel, length=wWheel, outerRadius=rWheel, axis=0)
66#exu.Print(inertiaWheel)
67
68inertiaCar = InertiaCuboid(density=mCar/(lCar*wCar*hCar),sideLengths=[wCar, lCar, hCar])
69#exu.Print(inertiaCar)
70
71graphicsCar = graphics.Brick(centerPoint=[0,0,0],size=[wCar-1.1*wWheel, lCar, hCar],
72 color=graphics.color.steelblue)
73[nCar,bCar]=AddRigidBody(mainSys = mbs,
74 inertia = inertiaCar,
75 nodeType = str(exu.NodeType.RotationEulerParameters),
76 position = p0Car,
77 rotationMatrix = initialRotationCar,
78 angularVelocity = omega0Car,
79 velocity=v0Car,
80 gravity = g,
81 graphicsDataList = [graphicsCar])
82
83nWheels = 4
84markerWheels=[]
85markerCarAxles=[]
86oRollingDiscs=[]
87sAngularVelWheels=[]
88
89# car setup:
90# ^Y, lCar
91# | W2 +---+ W3
92# | | |
93# | | + | car center point
94# | | |
95# | W0 +---+ W1
96# +---->X, wCar
97
98#ground body and marker
99gGround = graphics.Brick(centerPoint=[4,4,-0.001],size=[12,12,0.002], color=graphics.color.lightgrey[0:3]+[0.2])
100oGround = mbs.AddObject(ObjectGround(visualization=VObjectGround(graphicsData=[gGround])))
101markerGround = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oGround, localPosition=[0,0,0]))
102
103if useGraphics:
104 sCarVel = mbs.AddSensor(SensorBody(bodyNumber=bCar, storeInternal=True, #fileName='solution/rollingDiscCarVel.txt',
105 outputVariableType = exu.OutputVariableType.Velocity))
106
107sPos=[]
108sTrail=[]
109sForce=[]
110
111
112for iWheel in range(nWheels):
113 frictionAngle = 0.25*np.pi #45°
114 if iWheel == 0 or iWheel == 3: #difference in diagonal
115 frictionAngle *= -1
116
117 #additional graphics for visualization of rotation (JUST FOR DRAWING!):
118 graphicsWheel = [graphics.Brick(centerPoint=[0,0,0],size=[wWheel*1.1,0.7*rWheel,0.7*rWheel], color=graphics.color.lightred)]
119 nCyl = 12
120 rCyl = 0.1*rWheel
121 for i in range(nCyl): #draw cylinders on wheels
122 iPhi = i/nCyl*2*np.pi
123 pAxis = np.array([0,rWheel*np.sin(iPhi),-rWheel*np.cos(iPhi)])
124 vAxis = [0.5*wWheel*np.cos(frictionAngle),0.5*wWheel*np.sin(frictionAngle),0]
125 vAxis2 = RotationMatrixX(iPhi)@vAxis
126 rColor = graphics.color.grey
127 if i >= nCyl/2: rColor = graphics.color.darkgrey
128 graphicsWheel += [graphics.Cylinder(pAxis=pAxis-vAxis2, vAxis=2*vAxis2, radius=rCyl,
129 color=rColor)]
130
131
132 dx = -0.5*wCar
133 dy = -0.5*lCar
134 if iWheel > 1: dy *= -1
135 if iWheel == 1 or iWheel == 3: dx *= -1
136
137 kRolling = 1e5
138 dRolling = kRolling*0.01
139
140 initialRotation = RotationMatrixZ(0)
141
142 #v0Wheel = Skew(omega0Wheel) @ initialRotationWheel @ [0,0,rWheel] #initial angular velocity of center point
143 v0Wheel = v0Car #approx.
144
145 pOff = [dx,dy,0]
146
147
148 #add wheel body
149 [n0,b0]=AddRigidBody(mainSys = mbs,
150 inertia = inertiaWheel,
151 nodeType = str(exu.NodeType.RotationEulerParameters),
152 position = VAdd(p0Wheel,pOff),
153 rotationMatrix = initialRotation, #np.diag([1,1,1]),
154 angularVelocity = omega0Wheel,
155 velocity=v0Wheel,
156 gravity = g,
157 graphicsDataList = graphicsWheel)
158
159 #markers for rigid body:
160 mWheel = mbs.AddMarker(MarkerBodyRigid(bodyNumber=b0, localPosition=[0,0,0]))
161 markerWheels += [mWheel]
162
163 mCarAxle = mbs.AddMarker(MarkerBodyRigid(bodyNumber=bCar, localPosition=pOff))
164 markerCarAxles += [mCarAxle]
165
166 lockedAxis0 = 0
167 if doBreaking: lockedAxis0 = 1
168 #if iWheel==0 or iWheel==1: freeAxis = 1 #lock rotation
169 mbs.AddObject(GenericJoint(markerNumbers=[mWheel,mCarAxle],rotationMarker1=initialRotation,
170 constrainedAxes=[1,1,1,lockedAxis0,1,1])) #revolute joint for wheel
171
172 #does not work, because revolute joint does not accept off-axis
173 #kSuspension = 1e4
174 #dSuspension = kSuspension*0.01
175 #mbs.AddObject(CartesianSpringDamper(markerNumbers=[mWheel,mCarAxle],stiffness=[0,0,kSuspension],damping=[0,0,dSuspension]))
176
177 nGeneric = mbs.AddNode(NodeGenericData(initialCoordinates=[0,0,0], numberOfDataCoordinates=3))
178 oRolling = mbs.AddObject(ObjectConnectorRollingDiscPenalty(markerNumbers=[markerGround, mWheel], nodeNumber = nGeneric,
179 discRadius=rWheel, dryFriction=[1.,0.], dryFrictionAngle=frictionAngle,
180 dryFrictionProportionalZone=1e-1,
181 rollingFrictionViscous=0.2*0,
182 contactStiffness=kRolling, contactDamping=dRolling,
183 visualization=VObjectConnectorRollingDiscPenalty(discWidth=wWheel, color=graphics.color.blue)))
184 oRollingDiscs += [oRolling]
185
186 strNum = str(iWheel)
187 sAngularVelWheels += [mbs.AddSensor(SensorBody(bodyNumber=b0, storeInternal=True,#fileName='solution/rollingDiscAngVelLocal'+strNum+'.txt',
188 outputVariableType = exu.OutputVariableType.AngularVelocityLocal))]
189
190 if useGraphics:
191 sPos+=[mbs.AddSensor(SensorBody(bodyNumber=b0, storeInternal=True,#fileName='solution/rollingDiscPos'+strNum+'.txt',
192 outputVariableType = exu.OutputVariableType.Position))]
193
194 sTrail+=[mbs.AddSensor(SensorObject(name='Trail'+strNum,objectNumber=oRolling, storeInternal=True,#fileName='solution/rollingDiscTrail'+strNum+'.txt',
195 outputVariableType = exu.OutputVariableType.Position))]
196
197 sForce+=[mbs.AddSensor(SensorObject(objectNumber=oRolling, storeInternal=True,#fileName='solution/rollingDiscForce'+strNum+'.txt',
198 outputVariableType = exu.OutputVariableType.ForceLocal))]
199
200
201torqueFactor = 100
202def UFBasicTorque(mbs, t, torque):
203 if t < 0.2:
204 return torque
205 else:
206 return [0,0,0]
207
208#takes as input the translational and angular velocity and outputs the velocities for all 4 wheels
209#wheel axis is mounted at x-axis; positive angVel rotates CCW in x/y plane viewed from top
210# car setup:
211# ^Y, lCar
212# | W2 +---+ W3
213# | | |
214# | | + | car center point
215# | | |
216# | W0 +---+ W1
217# +---->X, wCar
218#values given for wheel0/3: frictionAngle=-pi/4, wheel 1/2: frictionAngle=pi/4; dryFriction=[1,0] (looks in lateral (x) direction)
219#==>direction of axis of roll on ground of wheel0: [1,-1] and of wheel1: [1,1]
220def MecanumXYphi2WheelVelocities(xVel, yVel, angVel, R, Lx, Ly):
221 LxLy2 = (Lx+Ly)/2
222 mat = (1/R)*np.array([[ 1,-1, LxLy2],
223 [-1,-1,-LxLy2],
224 [-1,-1, LxLy2],
225 [ 1,-1,-LxLy2]])
226 return mat @ [xVel, yVel, angVel]
227
228#compute velocity trajectory
229def ComputeVelocity(t):
230 vel = [0,0,0] #vx, vy, angVel; these are the local velocities!!!
231 f=1
232 if t < 4:
233 vel = [f,0,0]
234 elif t < 8:
235 vel = [0,f,0]
236 elif t < 16:
237 vel = [0,0,0.125*np.pi]
238 elif t < 20:
239 vel = [f,0,0]
240 return vel
241
242pControl = 500
243#compute controlled torque; torque[0] contains wheel number
244def UFtorque(mbs, t, torque):
245 iWheel = int(torque[0]) #wheel number
246
247 v = ComputeVelocity(t) #desired velocity
248 vDesired = MecanumXYphi2WheelVelocities(v[0],v[1],v[2],rWheel,wCar,lCar)[iWheel]
249 vCurrent = mbs.GetSensorValues(sAngularVelWheels[iWheel])[0] #local x-axis = wheel axis
250
251 cTorque = pControl*(vDesired-vCurrent)
252 #print("W",iWheel, ": vDes=", vDesired, ", vCur=", vCurrent, ", torque=", cTorque)
253
254 return [cTorque,0,0]
255
256if False:
257 mbs.AddLoad(Torque(markerNumber=markerWheels[0],loadVector=[ torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
258 mbs.AddLoad(Torque(markerNumber=markerWheels[1],loadVector=[-torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
259 mbs.AddLoad(Torque(markerNumber=markerWheels[2],loadVector=[-torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
260 mbs.AddLoad(Torque(markerNumber=markerWheels[3],loadVector=[ torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
261
262if True:
263 for i in range(4):
264 mbs.AddLoad(Torque(markerNumber=markerWheels[i],loadVector=[ i,0,0], bodyFixed = True, loadVectorUserFunction=UFtorque))
265
266#mbs.AddSensor(SensorObject(objectNumber=oRolling, fileName='solution/rollingDiscTrailVel.txt',
267# outputVariableType = exu.OutputVariableType.VelocityLocal))
268
269
270mbs.Assemble()
271
272simulationSettings = exu.SimulationSettings() #takes currently set values or default values
273
274tEnd = 0.5
275if useGraphics:
276 tEnd = 0.5 #24
277
278h=0.002
279
280simulationSettings.timeIntegration.numberOfSteps = int(tEnd/h)
281simulationSettings.timeIntegration.endTime = tEnd
282#simulationSettings.solutionSettings.solutionWritePeriod = 0.01
283simulationSettings.solutionSettings.sensorsWritePeriod = 0.002
284simulationSettings.timeIntegration.verboseMode = 0
285simulationSettings.displayComputationTime = False
286simulationSettings.displayStatistics = False
287
288simulationSettings.timeIntegration.generalizedAlpha.useIndex2Constraints = True
289simulationSettings.timeIntegration.generalizedAlpha.useNewmark = True
290simulationSettings.timeIntegration.generalizedAlpha.spectralRadius = 0.5#0.5
291simulationSettings.timeIntegration.generalizedAlpha.computeInitialAccelerations=True
292
293simulationSettings.timeIntegration.newton.useModifiedNewton = True
294simulationSettings.timeIntegration.discontinuous.ignoreMaxIterations = False #reduce step size for contact switching
295simulationSettings.timeIntegration.discontinuous.iterationTolerance = 0.1
296
297SC.visualizationSettings.nodes.show = True
298SC.visualizationSettings.nodes.drawNodesAsPoint = False
299SC.visualizationSettings.nodes.showBasis = True
300SC.visualizationSettings.nodes.basisSize = 0.015
301
302#create animation:
303if useGraphics:
304 SC.visualizationSettings.window.renderWindowSize=[1920,1080]
305 SC.visualizationSettings.openGL.multiSampling = 4
306 if False:
307 simulationSettings.solutionSettings.recordImagesInterval = 0.05
308 SC.visualizationSettings.exportImages.saveImageFileName = "animation/frame"
309
310if useGraphics:
311 exu.StartRenderer()
312 mbs.WaitForUserToContinue()
313
314mbs.SolveDynamic(simulationSettings)
315
316p0=mbs.GetObjectOutputBody(bCar, exu.OutputVariableType.Position, localPosition=[0,0,0])
317exu.Print('solution of mecanumWheelRollingDiscTest=',p0[0]) #use x-coordinate
318
319exudynTestGlobals.testError = p0[0] - (0.2714267238324345) #2020-06-20: 0.2714267238324345
320exudynTestGlobals.testResult = p0[0]
321
322
323if useGraphics:
324 SC.WaitForRenderEngineStopFlag()
325 exu.StopRenderer() #safely close rendering window!
326
327##++++++++++++++++++++++++++++++++++++++++++++++q+++++++
328#plot results
329if useGraphics:
330
331
332 mbs.PlotSensor(sTrail, componentsX=[0]*4, components=[1]*4, title='wheel trails', closeAll=True,
333 markerStyles=['x ','o ','^ ','D '], markerSizes=12)
334 mbs.PlotSensor(sForce, components=[1]*4, title='wheel forces')