Rigid bodies and joints

Notebook python/Notebooks/tutorialRigidBody.ipynb - the same model with the Create functions is python/Notebooks/tutorialRigidBodyCreate.ipynb.

A double pendulum in 3D: two rigid bodies, the first attached to the ground with a revolute joint about \(z\), the second to the first with a revolute joint about \(x\), under gravity. The joints are built from markers and joint objects, which shows what the Create functions do and works for any kind of body, also flexible ones; the outputs below each cell are those of the last run.

import exudyn as exu
from exudyn.utilities import InertiaCuboid, MarkerBodyRigid, GenericJoint, VObjectJointGeneric, \
                             ObjectJointRevoluteZ, VObjectJointRevoluteZ, SensorBody, RotationMatrixX, RotationMatrixY
from exudyn.interactive import ShowImage
import exudyn.graphics as graphics
import numpy as np

SC = exu.SystemContainer()
mbs = SC.AddSystem()

g = [0,-9.81,0]     #gravity
L = 1               #length
w = 0.1             #width
oGround = mbs.CreateGround(graphicsDataList=[graphics.CheckerBoard(point=[0.5,-1.6,0], normal=[0,1,0], size=3)])

The first link, a rigid body with its center of mass moved by \(-L/4\) along its axis; CreateRigidBody adds a node, a body and its gravity load. Its frame is at \([L/2,0,0]\), given as homogeneous transformation exu.HT.

iCube0 = InertiaCuboid(density=5000, sideLengths=[L,w,w]).Translated([-0.25*L,0,0])
graphicsBody0 = graphics.RigidLink(p0=[-0.5*L,0,0], p1=[0.5*L,0,0], axis0=[0,0,1], axis1=[0,0,0],
                                   radius=[0.5*w,0.5*w], thickness=w, width=[1.2*w,1.2*w], color=graphics.color.red)
b0 = mbs.CreateRigidBody(inertia=iCube0, referenceHT=exu.HT(translation=[0.5*L,0,0]), gravity=g,
                         graphicsDataList=[graphics.Basis(origin=iCube0.com, length=2*w), graphicsBody0])

A joint connects two markers, here two rigid body markers: on the ground at \([0,0,0]\) and on the body at its left end. The joint axes are those of the markers’ frames; a marker turns its frame by its localHT.

Option 1 is the GenericJoint, which defines any joint with translations and rotations fixed or free - here all but the rotation about \(z\). Option 2 is ObjectJointRevoluteZ, a free rotation about the local \(z\)-axis of marker 0; any other axis is chosen by the rotation of the markers’ localHT, e.g. MarkerBodyRigid(bodyNumber=b0, localHT=exu.HT().SetRotationY(0.5*np.pi)).

markerGround = mbs.AddMarker(MarkerBodyRigid(bodyNumber=oGround, localPosition=[0,0,0]))
markerBody0J0 = mbs.AddMarker(MarkerBodyRigid(bodyNumber=b0, localPosition=[-0.5*L,0,0]))

#option 1:
oJoint0 = mbs.AddObject(GenericJoint(markerNumbers=[markerGround, markerBody0J0], constrainedAxes=[1,1,1,1,1,0],
                                     visualization=VObjectJointGeneric(axesRadius=0.2*w, axesLength=1.4*w)))
#option 2, the same joint:
#oJoint0 = mbs.AddObject(ObjectJointRevoluteZ(markerNumbers=[markerGround, markerBody0J0],
#                                             visualization=VObjectJointRevoluteZ(axisRadius=0.2*w, axisLength=1.4*w)))

A wrong marker position shows in the render window: with localPosition=[-0.4*L,0,0], the two parts of the joint would be drawn 0.1*L apart.

The second link and its joint about \(x\), again from two markers; the generic joint leaves the rotation about \(x\) free:

graphicsBody1 = graphics.RigidLink(p0=[0,0,-0.5*L], p1=[0,0,0.5*L], axis0=[1,0,0], axis1=[0,0,0],
                                   radius=[0.06,0.05], thickness=0.1, width=[0.12,0.12], color=graphics.color.lightgreen)
b1 = mbs.CreateRigidBody(inertia=InertiaCuboid(density=5000, sideLengths=[0.1,0.1,1]),
                         referenceHT=exu.HT(translation=[L,0,0.5*L]), gravity=g, graphicsDataList=[graphicsBody1])

markerBody0J1 = mbs.AddMarker(MarkerBodyRigid(bodyNumber=b0, localPosition=[0.5*L,0,0]))
markerBody1J0 = mbs.AddMarker(MarkerBodyRigid(bodyNumber=b1, localPosition=[0,0,-0.5*L]))
oJoint1 = mbs.AddObject(GenericJoint(markerNumbers=[markerBody0J1, markerBody1J0], constrainedAxes=[1,1,1,0,1,1],
                                     visualization=VObjectJointGeneric(axesRadius=0.2*w, axesLength=1.4*w)))

sens1 = mbs.AddSensor(SensorBody(bodyNumber=b1, localPosition=[0,0,0.5*L], storeInternal=True,
                                 outputVariableType=exu.OutputVariableType.Position))
mbs.Assemble()
dof = mbs.ComputeSystemDegreeOfFreedom(verbose=True)
ODE2 coordinates          = 14
total constraints         = 14
redundant constraints     = 0
pure algebraic constraints= 2
degree of freedom         = 2

The model in its reference configuration, with the joint axes:

SC.visualizationSettings.nodes.show = False
SC.visualizationSettings.loads.show = False
SC.visualizationSettings.connectors.showJointAxes = True
SC.visualizationSettings.general.showSolverInformation = False
view = RotationMatrixX(-0.4) @ RotationMatrixY(-0.5)
image = ShowImage(SC, size=[1280,960], modelRotation=view)
../../../_images/tutorialRigidBody_0.png

Four seconds with the index 2 trapezoidal rule, and the tip position over time:

tEnd = 4
h = 1e-3
simulationSettings = exu.SimulationSettings()
simulationSettings.timeIntegration.numberOfSteps = int(tEnd/h)
simulationSettings.timeIntegration.endTime = tEnd
simulationSettings.solution.file.writePeriod = 0.005

mbs.SolveDynamic(simulationSettings=simulationSettings, solverType=exu.DynamicSolverType.TrapezoidalIndex2)
mbs.PlotSensor(sensorNumbers=[sens1], components=[1], closeAll=True)
../../../_images/tutorialRigidBody_1.png

In a script, mbs.SolutionViewer() shows the stored motion forward and backward in the render window.