laserScannerTest.py
You can view and download this file on Github: laserScannerTest.py
1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2# This is an EXUDYN example
3#
4# Details: Simple vehicle model with 'rotating' laser scanner
5#
6# Author: Johannes Gerstmayr
7# Date: 2023-04-11
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#
11#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
12
13import exudyn
14import exudyn as exu
15from exudyn.utilities import * #includes itemInterface and rigidBodyUtilities
16import exudyn.graphics as graphics #only import if it does not conflict
17from exudyn.robotics.utilities import AddLidar
18
19import numpy as np
20from math import sin, cos, tan
21
22useGraphics = True #without test
23#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
24#you can erase the following lines and all exudynTestGlobals related operations if this is not intended to be used as TestModel:
25try: #only if called from test suite
26 from modelUnitTests import exudynTestGlobals #for globally storing test results
27 useGraphics = exudynTestGlobals.useGraphics
28except:
29 class ExudynTestGlobals:
30 pass
31 exudynTestGlobals = ExudynTestGlobals()
32#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
33#useGraphics=False
34
35SC = exu.SystemContainer()
36mbs = SC.AddSystem()
37
38g = [0,0,-9.81] #gravity in m/s^2
39
40doBreaking = False
41
42#++++++++++++++++++++++++++++++
43#wheel parameters:
44rhoWheel = 500 #density kg/m^3
45rWheel = 0.4 #radius of disc in m
46wWheel = 0.2 #width of disc in m, just for drawing
47p0Wheel = [0,0,rWheel] #origin of disc center point at reference, such that initial contact point is at [0,0,0]
48initialRotationCar = RotationMatrixZ(0)
49
50v0 = -5*0 #initial car velocity in y-direction
51omega0Wheel = [v0/rWheel,0,0] #initial angular velocity around z-axis
52
53#v0 = [0,0,0] #initial translational velocity
54#exu.Print("v0Car=",v0)
55
56#++++++++++++++++++++++++++++++
57#car parameters:
58p0Car = [0,0,rWheel] #origin of disc center point at reference, such that initial contact point is at [0,0,0]
59lCar = 3 #y-direction
60wCar = 3 #x-direction
61hCar = rWheel #z-direction
62mCar = 500
63omega0Car = [0,0,0] #initial angular velocity around z-axis
64v0Car = [0,-v0,0] #initial velocity of car center point
65
66#inertia for infinitely small ring:
67inertiaWheel = InertiaCylinder(density=rhoWheel, length=wWheel, outerRadius=rWheel, axis=0)
68#exu.Print(inertiaWheel)
69
70inertiaCar = InertiaCuboid(density=mCar/(lCar*wCar*hCar),sideLengths=[wCar, lCar, hCar])
71#exu.Print(inertiaCar)
72#
73rLidar = 0.5*rWheel
74pLidar1 = [-wCar*0.5-rLidar, lCar*0.5+rWheel+rLidar,hCar*0.5]
75pLidar2 = [ wCar*0.5+rLidar,-lCar*0.5-rWheel-rLidar,hCar*0.5]
76graphicsCar = [graphics.Brick(centerPoint=[0,0,0],size=[wCar-1.1*wWheel, lCar+2*rWheel, hCar],
77 color=graphics.color.steelblue)]
78graphicsCar += [graphics.Cylinder(pAxis=pLidar1, vAxis=[0,0,0.5*rLidar], radius=rLidar, clor=graphics.color.darkgrey)]
79graphicsCar += [graphics.Cylinder(pAxis=pLidar2, vAxis=[0,0,0.5*rLidar], radius=rLidar, clor=graphics.color.darkgrey)]
80
81[nCar,bCar]=AddRigidBody(mainSys = mbs,
82 inertia = inertiaCar,
83 nodeType = str(exu.NodeType.RotationEulerParameters),
84 position = p0Car,
85 rotationMatrix = initialRotationCar,
86 angularVelocity = omega0Car,
87 velocity=v0Car,
88 gravity = g,
89 graphicsDataList = graphicsCar)
90
91markerCar = mbs.AddMarker(MarkerBodyRigid(bodyNumber=bCar, localPosition=[0,0,hCar*0.5]))
92
93
94markerCar1 = mbs.AddMarker(MarkerBodyRigid(bodyNumber=bCar, localPosition=pLidar1))
95markerCar2 = mbs.AddMarker(MarkerBodyRigid(bodyNumber=bCar, localPosition=pLidar2))
96
97
98nWheels = 4
99markerWheels=[]
100markerCarAxles=[]
101oRollingDiscs=[]
102sAngularVelWheels=[]
103
104# car setup:
105# ^Y, lCar
106# | W2 +---+ W3
107# | | |
108# | | + | car center point
109# | | |
110# | W0 +---+ W1
111# +---->X, wCar
112
113#ground body and marker
114LL = 8
115gGround = graphics.CheckerBoard(point=[0.25*LL,0.25*LL,0],size=2*LL)
116
117#obstacles:
118zz=1
119gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[0,8,0.5*zz],size=[2*zz,zz,1*zz], color=graphics.color.dodgerblue), gGround)
120gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[8,6,1.5*zz],size=[zz,2*zz,3*zz], color=graphics.color.dodgerblue), gGround)
121gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[4,-4,0.5*zz],size=[2*zz,zz,1*zz], color=graphics.color.dodgerblue), gGround)
122gGround = graphics.MergeTriangleLists(graphics.Cylinder(pAxis=[8,0,0],vAxis=[0,0,zz], radius=1.5, color=graphics.color.dodgerblue, nTiles=64), gGround)
123
124#walls:
125tt=0.2
126gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[0.25*LL,0.25*LL-LL,0.5*zz],size=[2*LL,tt,zz], color=graphics.color.dodgerblue), gGround)
127gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[0.25*LL,0.25*LL+LL,0.5*zz],size=[2*LL,tt,zz], color=graphics.color.dodgerblue), gGround)
128gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[0.25*LL-LL,0.25*LL,0.5*zz],size=[tt,2*LL,zz], color=graphics.color.dodgerblue), gGround)
129gGround = graphics.MergeTriangleLists(graphics.Brick(centerPoint=[0.25*LL+LL,0.25*LL,0.5*zz],size=[tt,2*LL,zz], color=graphics.color.dodgerblue), gGround)
130
131
132oGround = mbs.AddObject(ObjectGround(visualization=VObjectGround(graphicsData=[gGround])))
133mGround = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oGround, localPosition=[0,0,0]))
134
135
136#%%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
137#set up general contact geometry where sensors measure
138[meshPoints, meshTrigs] = graphics.ToPointsAndTrigs(gGround)
139
140ngc = mbs.CreateDistanceSensorGeometry(meshPoints, meshTrigs, rigidBodyMarkerIndex=mGround, searchTreeCellSize=[8,8,1])
141
142#single sensor:
143# sDistanceSphere = mbs.CreateDistanceSensor(ngc, positionOrMarker=markerCar2, dirSensor=dirSensor2,
144# minDistance=0, maxDistance=maxDistance, measureVelocity=True,
145# cylinderRadius=0, storeInternal=True, addGraphicsObject=True,
146# selectedTypeIndex=exu.ContactTypeIndex.IndexTrigsRigidBodyBased,
147# color=graphics.color.red)
148
149maxDistance = 100 #max. distance of sensors; just large enough to reach everything; take care, in zoom all it will show this large area
150
151#note that lidar sensors seem to be drawn wrong in the initialization; however, this is because the initial distance
152# is zero which means that the sensor is drawn into the negative direction during initialization!!!
153sLidar = AddLidar(mbs, generalContactIndex=ngc, positionOrMarker=markerCar2, minDistance=0, maxDistance=maxDistance,
154 numberOfSensors=100,angleStart=1.*pi, angleEnd=2.5*pi, inclination=0,
155 lineLength=1, storeInternal=True, color=graphics.color.lawngreen )
156
157AddLidar(mbs, generalContactIndex=ngc, positionOrMarker=markerCar2, minDistance=0, maxDistance=maxDistance,
158 numberOfSensors=100,angleStart=1.*pi, angleEnd=2.5*pi, inclination=-4/180*pi,
159 lineLength=1, storeInternal=True, color=graphics.color.grey )
160
161sLidarInc = AddLidar(mbs, generalContactIndex=ngc, positionOrMarker=markerCar2, minDistance=0, maxDistance=maxDistance,
162 numberOfSensors=100,angleStart=1.*pi, angleEnd=2.5*pi, inclination= 4/180*pi,
163 lineLength=1, storeInternal=True, color=graphics.color.grey )
164
165AddLidar(mbs, generalContactIndex=ngc, positionOrMarker=markerCar2, minDistance=0, maxDistance=maxDistance,
166 numberOfSensors=100,angleStart=1.*pi, angleEnd=2.5*pi, inclination= 8/180*pi,
167 lineLength=1, storeInternal=True, color=graphics.color.grey )
168
169AddLidar(mbs, generalContactIndex=ngc, positionOrMarker=markerCar2, minDistance=0, maxDistance=maxDistance,
170 numberOfSensors=100,angleStart=1.*pi, angleEnd=2.5*pi, inclination=12/180*pi,
171 lineLength=1, storeInternal=True, color=graphics.color.grey )
172
173AddLidar(mbs, generalContactIndex=ngc, positionOrMarker=markerCar1, minDistance=0, maxDistance=maxDistance,
174 numberOfSensors=100,angleStart=0*pi, angleEnd=1.5*pi,
175 lineLength=1, storeInternal=True, color=graphics.color.red) #, rotation=RotationMatrixX(2/180*pi))
176
177#%%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
178
179if useGraphics:
180 sCarVel = mbs.AddSensor(SensorBody(bodyNumber=bCar, storeInternal=True, #fileName='solution/rollingDiscCarVel.txt',
181 outputVariableType = exu.OutputVariableType.Velocity))
182
183sPos=[]
184sTrail=[]
185sForce=[]
186
187
188for iWheel in range(nWheels):
189 frictionAngle = 0.25*np.pi #45°
190 if iWheel == 0 or iWheel == 3: #difference in diagonal
191 frictionAngle *= -1
192
193 #additional graphics for visualization of rotation (JUST FOR DRAWING!):
194 graphicsWheel = [graphics.Brick(centerPoint=[0,0,0],size=[wWheel*1.1,0.7*rWheel,0.7*rWheel], color=graphics.color.lightred)]
195 nCyl = 12
196 rCyl = 0.1*rWheel
197 for i in range(nCyl): #draw cylinders on wheels
198 iPhi = i/nCyl*2*np.pi
199 pAxis = np.array([0,rWheel*np.sin(iPhi),-rWheel*np.cos(iPhi)])
200 vAxis = [0.5*wWheel*np.cos(frictionAngle),0.5*wWheel*np.sin(frictionAngle),0]
201 vAxis2 = RotationMatrixX(iPhi)@vAxis
202 rColor = graphics.color.grey
203 if i >= nCyl/2: rColor = graphics.color.darkgrey
204 graphicsWheel += [graphics.Cylinder(pAxis=pAxis-vAxis2, vAxis=2*vAxis2, radius=rCyl,
205 color=rColor)]
206
207
208 dx = -0.5*wCar
209 dy = -0.5*lCar
210 if iWheel > 1: dy *= -1
211 if iWheel == 1 or iWheel == 3: dx *= -1
212
213 kRolling = 1e5
214 dRolling = kRolling*0.01
215
216 initialRotation = RotationMatrixZ(0)
217
218 #v0Wheel = Skew(omega0Wheel) @ initialRotationWheel @ [0,0,rWheel] #initial angular velocity of center point
219 v0Wheel = v0Car #approx.
220
221 pOff = [dx,dy,0]
222
223
224 #add wheel body
225 [n0,b0]=AddRigidBody(mainSys = mbs,
226 inertia = inertiaWheel,
227 nodeType = str(exu.NodeType.RotationEulerParameters),
228 position = VAdd(p0Wheel,pOff),
229 rotationMatrix = initialRotation, #np.diag([1,1,1]),
230 angularVelocity = omega0Wheel,
231 velocity=v0Wheel,
232 gravity = g,
233 graphicsDataList = graphicsWheel)
234
235 #markers for rigid body:
236 mWheel = mbs.AddMarker(MarkerBodyRigid(bodyNumber=b0, localPosition=[0,0,0]))
237 markerWheels += [mWheel]
238
239 mCarAxle = mbs.AddMarker(MarkerBodyRigid(bodyNumber=bCar, localPosition=pOff))
240 markerCarAxles += [mCarAxle]
241
242 lockedAxis0 = 0
243 if doBreaking: lockedAxis0 = 1
244 #if iWheel==0 or iWheel==1: freeAxis = 1 #lock rotation
245 mbs.AddObject(GenericJoint(markerNumbers=[mWheel,mCarAxle],rotationMarker1=initialRotation,
246 constrainedAxes=[1,1,1,lockedAxis0,1,1])) #revolute joint for wheel
247
248 #does not work, because revolute joint does not accept off-axis
249 #kSuspension = 1e4
250 #dSuspension = kSuspension*0.01
251 #mbs.AddObject(CartesianSpringDamper(markerNumbers=[mWheel,mCarAxle],stiffness=[0,0,kSuspension],damping=[0,0,dSuspension]))
252
253 nGeneric = mbs.AddNode(NodeGenericData(initialCoordinates=[0,0,0], numberOfDataCoordinates=3))
254 oRolling = mbs.AddObject(ObjectConnectorRollingDiscPenalty(markerNumbers=[mGround, mWheel], nodeNumber = nGeneric,
255 discRadius=rWheel, dryFriction=[1.,0.], dryFrictionAngle=frictionAngle,
256 dryFrictionProportionalZone=1e-1,
257 rollingFrictionViscous=0.2*0,
258 contactStiffness=kRolling, contactDamping=dRolling,
259 visualization=VObjectConnectorRollingDiscPenalty(discWidth=wWheel, color=graphics.color.blue)))
260 oRollingDiscs += [oRolling]
261
262 strNum = str(iWheel)
263 sAngularVelWheels += [mbs.AddSensor(SensorBody(bodyNumber=b0, storeInternal=True,#fileName='solution/rollingDiscAngVelLocal'+strNum+'.txt',
264 outputVariableType = exu.OutputVariableType.AngularVelocityLocal))]
265
266 if useGraphics:
267 sPos+=[mbs.AddSensor(SensorBody(bodyNumber=b0, storeInternal=True,#fileName='solution/rollingDiscPos'+strNum+'.txt',
268 outputVariableType = exu.OutputVariableType.Position))]
269
270 sTrail+=[mbs.AddSensor(SensorObject(name='Trail'+strNum,objectNumber=oRolling, storeInternal=True,#fileName='solution/rollingDiscTrail'+strNum+'.txt',
271 outputVariableType = exu.OutputVariableType.Position))]
272
273 sForce+=[mbs.AddSensor(SensorObject(objectNumber=oRolling, storeInternal=True,#fileName='solution/rollingDiscForce'+strNum+'.txt',
274 outputVariableType = exu.OutputVariableType.ForceLocal))]
275
276
277torqueFactor = 100
278def UFBasicTorque(mbs, t, torque):
279 if t < 0.2:
280 return torque
281 else:
282 return [0,0,0]
283
284#takes as input the translational and angular velocity and outputs the velocities for all 4 wheels
285#wheel axis is mounted at x-axis; positive angVel rotates CCW in x/y plane viewed from top
286# car setup:
287# ^Y, lCar
288# | W2 +---+ W3
289# | | |
290# | | + | car center point
291# | | |
292# | W0 +---+ W1
293# +---->X, wCar
294#values given for wheel0/3: frictionAngle=-pi/4, wheel 1/2: frictionAngle=pi/4; dryFriction=[1,0] (looks in lateral (x) direction)
295#==>direction of axis of roll on ground of wheel0: [1,-1] and of wheel1: [1,1]
296def MecanumXYphi2WheelVelocities(xVel, yVel, angVel, R, Lx, Ly):
297 LxLy2 = (Lx+Ly)/2
298 mat = (1/R)*np.array([[ 1,-1, LxLy2],
299 [-1,-1,-LxLy2],
300 [-1,-1, LxLy2],
301 [ 1,-1,-LxLy2]])
302 return mat @ [xVel, yVel, angVel]
303
304#compute velocity trajectory
305def ComputeVelocity(t):
306 vel = [0,0,0] #vx, vy, angVel; these are the local velocities!!!
307 f=1
308 if t < 4:
309 vel = [f,0,0]
310 elif t < 8:
311 vel = [0,f,0]
312 elif t < 16:
313 vel = [0,0,0.125*np.pi]
314 elif t < 20:
315 vel = [f,0,0]
316 return vel
317
318pControl = 500
319#compute controlled torque; torque[0] contains wheel number
320def UFtorque(mbs, t, torque):
321 iWheel = int(torque[0]) #wheel number
322
323 v = ComputeVelocity(t) #desired velocity
324 vDesired = MecanumXYphi2WheelVelocities(v[0],v[1],v[2],rWheel,wCar,lCar)[iWheel]
325 vCurrent = mbs.GetSensorValues(sAngularVelWheels[iWheel])[0] #local x-axis = wheel axis
326
327 cTorque = pControl*(vDesired-vCurrent)
328 #print("W",iWheel, ": vDes=", vDesired, ", vCur=", vCurrent, ", torque=", cTorque)
329
330 return [cTorque,0,0]
331
332if False:
333 mbs.AddLoad(Torque(markerNumber=markerWheels[0],loadVector=[ torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
334 mbs.AddLoad(Torque(markerNumber=markerWheels[1],loadVector=[-torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
335 mbs.AddLoad(Torque(markerNumber=markerWheels[2],loadVector=[-torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
336 mbs.AddLoad(Torque(markerNumber=markerWheels[3],loadVector=[ torqueFactor,0,0], bodyFixed = True, loadVectorUserFunction=UFBasicTorque))
337
338if True:
339 for i in range(4):
340 mbs.AddLoad(Torque(markerNumber=markerWheels[i],loadVector=[ i,0,0], bodyFixed = True, loadVectorUserFunction=UFtorque))
341
342#mbs.AddSensor(SensorObject(objectNumber=oRolling, fileName='solution/rollingDiscTrailVel.txt',
343# outputVariableType = exu.OutputVariableType.VelocityLocal))
344
345
346# print('start')
347mbs.Assemble()
348# print('end')
349
350simulationSettings = exu.SimulationSettings() #takes currently set values or default values
351
352tEnd = 0.5
353if useGraphics:
354 tEnd = 20#24
355
356h=0.002
357
358simulationSettings.timeIntegration.numberOfSteps = int(tEnd/h)
359simulationSettings.timeIntegration.endTime = tEnd
360#simulationSettings.solutionSettings.solutionWritePeriod = 0.01
361simulationSettings.solutionSettings.sensorsWritePeriod = 0.1
362simulationSettings.timeIntegration.verboseMode = 1
363simulationSettings.displayComputationTime = False
364simulationSettings.displayStatistics = False
365
366simulationSettings.timeIntegration.generalizedAlpha.useIndex2Constraints = True
367simulationSettings.timeIntegration.generalizedAlpha.useNewmark = True
368simulationSettings.timeIntegration.generalizedAlpha.spectralRadius = 0.5#0.5
369simulationSettings.timeIntegration.generalizedAlpha.computeInitialAccelerations=True
370
371simulationSettings.timeIntegration.newton.useModifiedNewton = True
372simulationSettings.timeIntegration.discontinuous.ignoreMaxIterations = False #reduce step size for contact switching
373simulationSettings.timeIntegration.discontinuous.iterationTolerance = 0.1
374simulationSettings.linearSolverType=exu.LinearSolverType.EigenSparse
375
376speedup=True
377if speedup:
378 simulationSettings.timeIntegration.discontinuous.ignoreMaxIterations = False #reduce step size for contact switching
379 simulationSettings.timeIntegration.discontinuous.iterationTolerance = 0.1
380
381SC.visualizationSettings.general.graphicsUpdateInterval = 0.01
382SC.visualizationSettings.nodes.show = True
383SC.visualizationSettings.nodes.drawNodesAsPoint = False
384SC.visualizationSettings.nodes.showBasis = True
385SC.visualizationSettings.nodes.basisSize = 0.015
386
387SC.visualizationSettings.openGL.lineWidth = 2
388SC.visualizationSettings.openGL.shadow = 0.3
389SC.visualizationSettings.openGL.multiSampling = 4
390SC.visualizationSettings.openGL.perspective = 0.7
391# useGraphics=True
392#create animation:
393if useGraphics:
394 SC.visualizationSettings.window.renderWindowSize=[1920,1080]
395 SC.visualizationSettings.openGL.multiSampling = 4
396
397 if False: #save images
398 simulationSettings.solutionSettings.sensorsWritePeriod = 0.01 #to avoid laggy visualization
399 simulationSettings.solutionSettings.recordImagesInterval = 0.04
400 SC.visualizationSettings.exportImages.saveImageFileName = "images/frame"
401
402if useGraphics:
403 exu.StartRenderer()
404 mbs.WaitForUserToContinue()
405
406mbs.SolveDynamic(simulationSettings)
407
408p0=mbs.GetObjectOutputBody(bCar, exu.OutputVariableType.Position, localPosition=[0,0,0])
409
410u = 0+p0[0]
411for s in sLidar+sLidarInc:
412 u += mbs.GetSensorValues(s)
413
414u/=len(sLidar+sLidarInc)*10
415
416exu.Print('solution of mecanumWheelRollingDiscTest=',u)
417exudynTestGlobals.testError = u - (0.27142672383243405) #2020-06-20: 0.2714267238324345
418exudynTestGlobals.testResult = u
419
420
421if useGraphics:
422 SC.WaitForRenderEngineStopFlag()
423 exu.StopRenderer() #safely close rendering window!
424
425##++++++++++++++++++++++++++++++++++++++++++++++q+++++++
426#plot results
427if useGraphics and False:
428
429
430 mbs.PlotSensor(sTrail, componentsX=[0]*4, components=[1]*4, title='wheel trails', closeAll=True,
431 markerStyles=['x ','o ','^ ','D '], markerSizes=12)
432 mbs.PlotSensor(sForce, components=[1]*4, title='wheel forces')