reevingSystemOpen.py
You can view and download this file on Github: reevingSystemOpen.py
1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2# This is an EXUDYN example
3#
4# Details: Example to calculate rope line of reeving system
5#
6# Author: Johannes Gerstmayr
7# Date: 2022-02-02
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 as exu
14from exudyn.itemInterface import *
15from exudyn.utilities import * #includes itemInterface and rigidBodyUtilities
16import exudyn.graphics as graphics #only import if it does not conflict
17from exudyn.beams import *
18from exudyn.robotics import *
19
20import numpy as np
21from math import sin, cos, pi, sqrt , asin, acos, atan2
22import copy
23
24SC = exu.SystemContainer()
25mbs = SC.AddSystem()
26
27#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28#+++ MOTION PLANNING and TRAJECTORIES +++++++++++++++++++++++++++++++++++++++++++++
29#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
30#**function: Compute parameters for optimal trajectory using given duration and distance
31#**notes: DEPRECATED, DO NOT USE - moved to robotics.motion
32#**input: duration in seconds and distance in meters or radians
33#**output: returns [vMax, accMax] with maximum velocity and maximum acceleration to achieve given trajectory
34def ConstantAccelerationParameters(duration, distance):
35 accMax = 4*distance/duration**2
36 vMax = (accMax * distance)**0.5
37 return [vMax, accMax]
38
39#**function: Compute angle / displacement s, velocity v and acceleration a
40#**input:
41# t: current time to compute values
42# tStart: start time of profile
43# sStart: start offset of path
44# duration: duration of profile
45# distance: total distance (of path) of profile
46#**notes: DEPRECATED, DO NOT USE - moved to robotics.motion
47#**output: [s, v, a] with path s, velocity v and acceleration a for constant acceleration profile; before tStart, solution is [0,0,0] while after duration, solution is [sStart+distance, 0, 0]
48def ConstantAccelerationProfile(t, tStart, sStart, duration, distance):
49 [vMax, accMax] = ConstantAccelerationParameters(duration, distance)
50
51 s = sStart
52 v = 0
53 a = 0
54
55 x = t-tStart
56 if x < 0:
57 s=0
58 elif x < 0.5*duration:
59 s = sStart + 0.5*accMax*x**2
60 v = x*accMax
61 a = accMax
62 elif x < duration:
63 s = sStart + distance - 0.5*accMax * (duration-x)**2
64 v = (duration - x)*accMax
65 a = -accMax
66 else:
67 s = sStart + distance
68
69 return [s, v, a]
70
71#**function: Compute joint value, velocity and acceleration for given robotTrajectory['PTP'] of point-to-point type, evaluated for current time t and joint number
72#**input:
73# t: time to evaluate trajectory
74# robotTrajectory: dictionary to describe trajectory; in PTP case, either use 'time' points, or 'time' and 'duration', or 'time' and 'maxVelocity' and 'maxAccelerations' in all consecutive points; 'maxVelocities' and 'maxAccelerations' must be positive nonzero values that limit velocities and accelerations;
75# joint: joint number for which the trajectory shall be evaluated
76#**output: for current time t it returns [s, v, a] with path s, velocity v and acceleration a for current acceleration profile; outside of profile, it returns [0,0,0] !
77#**notes: DEPRECATED, DO NOT USE - moved to robotics.motion
78#**example:
79# q0 = [0,0,0,0,0,0] #initial configuration
80# q1 = [8,5,2,0,2,1] #other configuration
81# PTP =[]
82# PTP+=[{'q':q0,
83# 'time':0}]
84# PTP+=[{'q':q1,
85# 'time':0.5}]
86# PTP+=[{'q':q1,
87# 'time':1e6}] #forever
88# RT={'PTP':PTP}
89# [u,v,a] = MotionInterpolator(t=0.5, robotTrajectory=RT, joint=1)
90def MotionInterpolator(t, robotTrajectory, joint):
91
92 n = len(robotTrajectory['PTP'])
93 if n < 2:
94 print("ERROR in MotionInterpolator: trajectory must have at least 2 points!")
95
96 i = 0
97 while (i < n) and (t >= robotTrajectory['PTP'][i]['time']):
98 i += 1
99
100 if (i==0) or (i==n):
101 return [0,0,0] #outside of trajectory
102
103 #i must be > 0 and < n now!
104 q0 = robotTrajectory['PTP'][i-1] #must always exist
105 q1 = robotTrajectory['PTP'][i] #must always exist
106
107 return ConstantAccelerationProfile(t, q0['time'], q0['q'][joint],
108 q1['time'] - q0['time'],
109 q1['q'][joint] - q0['q'][joint])
110
111
112
113#%%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
114#settings:
115useGraphics= True
116useContact = True
117useFriction = True
118# kProp = 10
119dryFriction = 2*0.5
120contactStiffness = 1e5
121contactDamping = 2e-3*contactStiffness
122
123wheelMass = 1
124wheelInertia = 0.01
125
126rotationDampingWheels = 0.01 #proportional to rotation speed
127torque = 1
128
129#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
130#create circles
131#complicated shape:
132nANCFnodes = 1*50
133stepSize = 5e-4
134tEnd = 10
135R=0.45
136preStretch=-0.002*0 #not needed here, system is open!
137circleList = [
138 [[ 0,-3 ],R,'L'],
139 [[ 0,0 ],R,'L'],
140 [[0.5,-0.8],R,'R'],
141 [[ 1,0 ],R,'L'],
142 [[ 1,-3 ],R,'L'],
143 ]
144
145#create motion profile:
146point0={'q':[0], #initial configuration
147 'time':0}
148point1={'q':[2.5/R],
149 'time':2}
150point2={'q':[-2.5/R],
151 'time':4}
152point3={'q':[0],
153 'time':5}
154pointLast={'q':[0], #add this a second time to stay this forever
155 'time':1e6} #forever
156RT={'PTP':[point0,point1,point2,point3,pointLast]}
157
158#%%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
159#create geometry:
160reevingDict = CreateReevingCurve(circleList, drawingLinesPerCircle = 64,
161 removeLastLine=False, #allows closed curve
162 numberOfANCFnodes=nANCFnodes, graphicsNodeSize= 0.01)
163
164del circleList[-1] #remove circles not needed for contact/visualization
165del circleList[0] #remove circles not needed for contact/visualization
166
167gList=[]
168if False: #visualize reeving curve, without simulation
169 gList = reevingDict['graphicsDataLines'] + reevingDict['graphicsDataCircles']
170
171oGround=mbs.AddObject(ObjectGround(referencePosition= [0,0,0],
172 visualization=VObjectGround(graphicsData= gList)))
173
174#%%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
175#create ANCF elements:
176
177gVec = np.array([0,-9.81,0]) # gravity
178E=1e9 # Young's modulus of ANCF element in N/m^2
179rhoBeam=1000 # density of ANCF element in kg/m^3
180b=0.002 # width of rectangular ANCF element in m
181h=0.002 # height of rectangular ANCF element in m
182A=b*h # cross sectional area of ANCF element in m^2
183I=b*h**3/12 # second moment of area of ANCF element in m^4
184dEI = 1e-3*E*I #bending proportional damping
185dEA = 1e-2*E*A #axial strain proportional damping
186
187dimZ = b #z.dimension
188
189cableTemplate = Cable2D(#physicsLength = L / nElements, #set in GenerateStraightLineANCFCable2D(...)
190 physicsMassPerLength = rhoBeam*A,
191 physicsBendingStiffness = E*I,
192 physicsAxialStiffness = E*A,
193 physicsBendingDamping = dEI,
194 physicsAxialDamping = dEA,
195 physicsReferenceAxialStrain = preStretch, #prestretch
196 visualization=VCable2D(drawHeight=h),
197 )
198
199ancf = PointsAndSlopes2ANCFCable2D(mbs, reevingDict['ancfPointsSlopes'], reevingDict['elementLengths'],
200 cableTemplate, massProportionalLoad=gVec,
201 #fixedConstraintsNode0=[1,1,1,1], fixedConstraintsNode1=[1,1,1,1],
202 firstNodeIsLastNode=False, graphicsSizeConstraints=0.01)
203
204#+++++++++++++++++++++++++++++++++++++++
205#add weights
206node0 = ancf[0][0]
207nodeL = ancf[0][-1]
208massLoad=2
209
210bMass0 = mbs.AddObject(ObjectMassPoint2D(physicsMass=massLoad,
211 nodeNumber=node0,
212 visualization=VMassPoint2D(graphicsData=[graphics.Sphere(radius=0.1, nTiles=32)])))
213bMassL = mbs.AddObject(ObjectMassPoint2D(physicsMass=massLoad,
214 nodeNumber=nodeL,
215 visualization=VMassPoint2D(graphicsData=[graphics.Sphere(radius=0.1, nTiles=32)])))
216
217mBody0=mbs.AddMarker(MarkerBodyPosition(bodyNumber=bMass0))
218mbs.AddLoad(Force(markerNumber=mBody0, loadVector=massLoad*gVec))
219mBodyL=mbs.AddMarker(MarkerBodyPosition(bodyNumber=bMassL))
220mbs.AddLoad(Force(markerNumber=mBodyL, loadVector=massLoad*gVec))
221
222#%%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
223#add contact:
224if useContact:
225
226 gContact = mbs.AddGeneralContact()
227 gContact.verboseMode = 1
228 gContact.frictionProportionalZone = 0.5
229 gContact.ancfCableUseExactMethod = False
230 gContact.ancfCableNumberOfContactSegments = 4
231 gContact
232 ssx = 32#32 #search tree size
233 ssy = 32#32 #search tree size
234 ssz = 1 #search tree size
235 gContact.SetSearchTreeCellSize(numberOfCells=[ssx,ssy,ssz])
236 #gContact.SetSearchTreeBox(pMin=np.array([-1,-1,-1]), pMax=np.array([4,1,1])) #automatically computed!
237
238 halfHeight = 0.5*h*0
239 dimZ= 0.01 #for drawing
240 # wheels = [{'center':wheelCenter0, 'radius':rWheel0-halfHeight, 'mass':mWheel},
241 # {'center':wheelCenter1, 'radius':rWheel1-halfHeight, 'mass':mWheel},
242 # {'center':rollCenter1, 'radius':rRoll-halfHeight, 'mass':mRoll}, #small mass for roll, not to influence beam
243 # ]
244 sWheelRot = [] #sensors for angular velocity
245
246 nGround = mbs.AddNode(NodePointGround())
247 mCoordinateGround = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nGround, coordinate=0))
248
249 for i, wheel in enumerate(circleList):
250 p = [wheel[0][0], wheel[0][1], 0] #position of wheel center
251 r = wheel[1]
252
253 rot0 = 0 #initial rotation
254 pRef = [p[0], p[1], rot0]
255 gList = [graphics.Cylinder(pAxis=[0,0,-dimZ],vAxis=[0,0,-dimZ], radius=r,
256 color= graphics.color.lightgrey, nTiles=64),
257 graphics.Arrow(pAxis=[0,0,0], vAxis=[0.9*r,0,0], radius=0.01*r, color=graphics.color.orange)]
258
259 omega0 = 0 #initial angular velocity
260 v0 = np.array([0,0,omega0])
261
262 nMass = mbs.AddNode(NodeRigidBody2D(referenceCoordinates=pRef, initialVelocities=v0,
263 visualization=VNodeRigidBody2D(drawSize=dimZ*2)))
264 oMass = mbs.AddObject(ObjectRigidBody2D(physicsMass=wheelMass, physicsInertia=wheelInertia,
265 nodeNumber=nMass, visualization=
266 VObjectRigidBody2D(graphicsData=gList)))
267 mNode = mbs.AddMarker(MarkerNodeRigid(nodeNumber=nMass))
268 mGroundWheel = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oGround, localPosition=p))
269
270 mbs.AddObject(RevoluteJoint2D(markerNumbers=[mGroundWheel, mNode]))
271 sWheelRot += [mbs.AddSensor(SensorNode(nodeNumber=nMass,
272 fileName='solution/wheel'+str(i)+'angVel.txt',
273 outputVariableType=exu.OutputVariableType.AngularVelocity))]
274
275 #add drive with prescribed velocity:
276 def UFvelocityDrive(mbs, t, itemNumber, lOffset): #time derivative of UFoffset
277 v = 10*t
278 vMax = 5
279 return max(v, vMax)
280
281 def UFmotionDrive(mbs, t, itemNumber, lOffset):
282 [u,v,a] = MotionInterpolator(t, robotTrajectory=RT, joint=0)
283 return u
284
285
286 velocityControl = False
287 if i == 1:
288 mCoordinateWheel = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nMass, coordinate=2))
289 if velocityControl:
290 mbs.AddObject(CoordinateConstraint(markerNumbers=[mCoordinateGround, mCoordinateWheel],
291 velocityLevel=True, offsetUserFunction_t=UFvelocityDrive))
292 else: #position control
293 mbs.AddObject(CoordinateConstraint(markerNumbers=[mCoordinateGround, mCoordinateWheel],
294 offsetUserFunction=UFmotionDrive))
295
296 if i > 0: #friction on rolls:
297 mCoordinateWheel = mbs.AddMarker(MarkerNodeCoordinate(nodeNumber=nMass, coordinate=2))
298 mbs.AddObject(CoordinateSpringDamper(markerNumbers=[mCoordinateGround, mCoordinateWheel],
299 damping=rotationDampingWheels,
300 visualization=VCoordinateSpringDamper(show=False)))
301
302 frictionMaterialIndex=0
303 gContact.AddSphereWithMarker(mNode, radius=r, contactStiffness=contactStiffness,
304 contactDamping=contactDamping, frictionMaterialIndex=frictionMaterialIndex)
305
306
307
308 for oIndex in ancf[1]:
309 gContact.AddANCFCable(objectIndex=oIndex, halfHeight=halfHeight, #halfHeight should be h/2, but then cylinders should be smaller
310 contactStiffness=contactStiffness, contactDamping=contactDamping,
311 frictionMaterialIndex=0)
312
313 frictionMatrix = np.zeros((2,2))
314 frictionMatrix[0,0]=int(useFriction)*dryFriction
315 frictionMatrix[0,1]=0 #no friction between some rolls and cable
316 frictionMatrix[1,0]=0 #no friction between some rolls and cable
317 gContact.SetFrictionPairings(frictionMatrix)
318
319
320mbs.Assemble()
321
322simulationSettings = exu.SimulationSettings() #takes currently set values or default values
323
324simulationSettings.linearSolverType = exu.LinearSolverType.EigenSparse
325simulationSettings.solutionSettings.coordinatesSolutionFileName = 'solution/coordinatesSolution.txt'
326simulationSettings.solutionSettings.writeSolutionToFile = True
327simulationSettings.solutionSettings.solutionWritePeriod = 0.005
328simulationSettings.solutionSettings.sensorsWritePeriod = 0.001
329#simulationSettings.displayComputationTime = True
330simulationSettings.parallel.numberOfThreads = 4 #use 4 to speed up for > 100 ANCF elements
331simulationSettings.displayStatistics = True
332
333simulationSettings.timeIntegration.endTime = tEnd
334simulationSettings.timeIntegration.numberOfSteps = int(tEnd/stepSize)
335simulationSettings.timeIntegration.stepInformation= 3+4+32#+128+256
336#simulationSettings.timeIntegration.newton.absoluteTolerance = 1e-4
337simulationSettings.timeIntegration.newton.relativeTolerance = 1e-6
338#simulationSettings.timeIntegration.discontinuous.iterationTolerance = 10
339simulationSettings.timeIntegration.newton.modifiedNewtonJacUpdatePerStep=True
340simulationSettings.timeIntegration.newton.maxIterations=16
341simulationSettings.timeIntegration.verboseMode = 1
342# simulationSettings.timeIntegration.adaptiveStepRecoveryIterations = 9
343simulationSettings.timeIntegration.adaptiveStepRecoverySteps = 40
344
345simulationSettings.timeIntegration.newton.useModifiedNewton = True
346simulationSettings.timeIntegration.generalizedAlpha.spectralRadius = 0.5
347simulationSettings.displayStatistics = True
348
349SC.visualizationSettings.general.circleTiling = 24
350SC.visualizationSettings.loads.show=False
351SC.visualizationSettings.nodes.defaultSize = 0.01
352SC.visualizationSettings.openGL.multiSampling = 4
353SC.visualizationSettings.openGL.lineWidth = 2
354
355# SC.visualizationSettings.general.useGradientBackground = True
356simulationSettings.solutionSettings.solutionInformation = 'elevator'
357# SC.visualizationSettings.general.textSize = 14
358
359SC.visualizationSettings.window.renderWindowSize = [1024,2000]
360
361if False:
362 SC.visualizationSettings.contour.outputVariableComponent=0
363 SC.visualizationSettings.contour.outputVariable=exu.OutputVariableType.ForceLocal
364
365#visualize contact:
366if False:
367 SC.visualizationSettings.contact.showSearchTree =True
368 SC.visualizationSettings.contact.showSearchTreeCells =True
369 SC.visualizationSettings.contact.showBoundingBoxes = True
370
371if useGraphics:
372 exu.StartRenderer()
373 mbs.WaitForUserToContinue()
374
375if True:
376 doDynamic = True
377 if doDynamic :
378 exu.SolveDynamic(mbs, simulationSettings) #183 Newton iterations, 0.114 seconds
379 else:
380 exu.SolveStatic(mbs, simulationSettings) #183 Newton iterations, 0.114 seconds
381
382
383if useGraphics and True:
384 SC.visualizationSettings.general.autoFitScene = False
385 SC.visualizationSettings.general.graphicsUpdateInterval=0.02
386 # from exudyn.interactive import SolutionViewer
387 # sol = LoadSolutionFile('solution/coordinatesSolution.txt', safeMode=True)#, maxRows=100)
388 # SolutionViewer(mbs, sol)
389 mbs.SolutionViewer()
390
391
392if useGraphics:
393 SC.WaitForRenderEngineStopFlag()
394 exu.StopRenderer() #safely close rendering window!
395
396 # if True:
397 # from exudyn.plot import PlotSensor
398 # PlotSensor(mbs, sensorNumbers=[sAngVel[0],sAngVel[1]], components=2, closeAll=True)
399 # PlotSensor(mbs, sensorNumbers=sMeasureRoll, components=1)