From 65ae4f53e32eef65813d3e92e3848d6ab146a895 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Tue, 15 Oct 2024 11:39:54 +0000 Subject: [PATCH] build based on 1848db7 --- dev/.documenter-siteinfo.json | 2 +- dev/components/index.html | 22 +- .../{99ce2bfc.svg => 672a23aa.svg} | 76 +- .../{8358228b.svg => 99cd2fcf.svg} | 68 +- dev/examples/free_motion/index.html | 6 +- .../gearbox/{549154bf.svg => bbc711da.svg} | 196 ++--- dev/examples/gearbox/index.html | 2 +- dev/examples/gyroscopic_effects/index.html | 2 +- .../{05bfe82a.svg => 1789cc45.svg} | 68 +- .../{2399a07e.svg => 8d311527.svg} | 156 ++-- .../{e1c9b07c.svg => 9e8f3844.svg} | 72 +- dev/examples/kinematic_loops/index.html | 8 +- .../pendulum/{bc93fb68.svg => 1019d232.svg} | 64 +- dev/examples/pendulum/12fb8a2f.svg | 48 ++ .../pendulum/{c70497ec.svg => 210d83bb.svg} | 256 +++--- .../pendulum/{bb1ef008.svg => 2fd21779.svg} | 188 ++--- .../pendulum/{b85f93fa.svg => 30b752fb.svg} | 72 +- .../pendulum/{b5bdcd1b.svg => 4525f341.svg} | 252 +++--- .../pendulum/{69d35a6e.svg => 7ff8455f.svg} | 56 +- .../pendulum/{8d4190d0.svg => 8f074caf.svg} | 68 +- dev/examples/pendulum/8f719bec.svg | 50 -- .../pendulum/{37a591d3.svg => bf755513.svg} | 264 +++---- dev/examples/pendulum/index.html | 20 +- .../{2f01ba00.svg => 2e591505.svg} | 80 +- .../{00b54e07.svg => 665f198f.svg} | 148 ++-- dev/examples/prescribed_pose/index.html | 4 +- .../quad/{2c7779fe.svg => 8580b0de.svg} | 248 +++--- .../quad/{4ecbc3d1.svg => afd013b0.svg} | 264 +++---- dev/examples/quad/index.html | 6 +- .../robot/{40d12378.svg => 65cbf37f.svg} | 732 +++++++++--------- .../robot/{7e13e637.svg => 83b5c6f6.svg} | 180 ++--- .../robot/{5740da77.svg => e47df0e2.svg} | 180 ++--- dev/examples/robot/index.html | 8 +- dev/examples/ropes_and_cables/index.html | 2 +- .../sensors/{59b51a10.svg => 31f4b3ea.svg} | 88 +-- dev/examples/sensors/index.html | 2 +- .../space/{1aa89ae7.svg => 312ba417.svg} | 160 ++-- dev/examples/space/index.html | 4 +- .../{4f0db690.svg => 0fe440b3.svg} | 72 +- dev/examples/spherical_pendulum/index.html | 4 +- .../{e05864c5.svg => a0059dcd.svg} | 216 +++--- dev/examples/spring_damper_system/index.html | 4 +- .../{ceba65f3.svg => 44e04024.svg} | 80 +- dev/examples/spring_mass_system/index.html | 4 +- dev/examples/suspension/index.html | 2 +- .../swing/{5a65a99e.svg => d0862839.svg} | 76 +- dev/examples/swing/index.html | 4 +- .../{7e46880f.svg => 22f191f1.svg} | 84 +- dev/examples/three_springs/index.html | 4 +- .../wheel/{98e12431.svg => 6f13c721.svg} | 44 +- .../wheel/{db3c5150.svg => d49d9ee8.svg} | 224 +++--- dev/examples/wheel/index.html | 6 +- dev/forces/index.html | 2 +- dev/frames/index.html | 2 +- dev/index.html | 2 +- dev/interfaces/index.html | 2 +- dev/joints/index.html | 6 +- dev/rendering/index.html | 4 +- dev/rotations/index.html | 2 +- dev/sensors/index.html | 4 +- .../{f393e660.svg => 1309f726.svg} | 170 ++-- .../{919aef15.svg => cb8d3622.svg} | 152 ++-- dev/trajectory_planning/index.html | 4 +- dev/urdf/index.html | 2 +- 64 files changed, 2648 insertions(+), 2650 deletions(-) rename dev/examples/free_motion/{99ce2bfc.svg => 672a23aa.svg} (89%) rename dev/examples/free_motion/{8358228b.svg => 99cd2fcf.svg} (93%) rename dev/examples/gearbox/{549154bf.svg => bbc711da.svg} (90%) rename dev/examples/kinematic_loops/{05bfe82a.svg => 1789cc45.svg} (89%) rename dev/examples/kinematic_loops/{2399a07e.svg => 8d311527.svg} (98%) rename dev/examples/kinematic_loops/{e1c9b07c.svg => 9e8f3844.svg} (91%) rename dev/examples/pendulum/{bc93fb68.svg => 1019d232.svg} (89%) create mode 100644 dev/examples/pendulum/12fb8a2f.svg rename dev/examples/pendulum/{c70497ec.svg => 210d83bb.svg} (89%) rename dev/examples/pendulum/{bb1ef008.svg => 2fd21779.svg} (90%) rename dev/examples/pendulum/{b85f93fa.svg => 30b752fb.svg} (88%) rename dev/examples/pendulum/{b5bdcd1b.svg => 4525f341.svg} (91%) rename dev/examples/pendulum/{69d35a6e.svg => 7ff8455f.svg} (90%) rename dev/examples/pendulum/{8d4190d0.svg => 8f074caf.svg} (88%) delete mode 100644 dev/examples/pendulum/8f719bec.svg rename dev/examples/pendulum/{37a591d3.svg => bf755513.svg} (96%) rename dev/examples/prescribed_pose/{2f01ba00.svg => 2e591505.svg} (91%) rename dev/examples/prescribed_pose/{00b54e07.svg => 665f198f.svg} (89%) rename dev/examples/quad/{2c7779fe.svg => 8580b0de.svg} (98%) rename dev/examples/quad/{4ecbc3d1.svg => afd013b0.svg} (95%) rename dev/examples/robot/{40d12378.svg => 65cbf37f.svg} (91%) rename dev/examples/robot/{7e13e637.svg => 83b5c6f6.svg} (92%) rename dev/examples/robot/{5740da77.svg => e47df0e2.svg} (91%) rename dev/examples/sensors/{59b51a10.svg => 31f4b3ea.svg} (93%) rename dev/examples/space/{1aa89ae7.svg => 312ba417.svg} (96%) rename dev/examples/spherical_pendulum/{4f0db690.svg => 0fe440b3.svg} (93%) rename dev/examples/spring_damper_system/{e05864c5.svg => a0059dcd.svg} (91%) rename dev/examples/spring_mass_system/{ceba65f3.svg => 44e04024.svg} (90%) rename dev/examples/swing/{5a65a99e.svg => d0862839.svg} (99%) rename dev/examples/three_springs/{7e46880f.svg => 22f191f1.svg} (93%) rename dev/examples/wheel/{98e12431.svg => 6f13c721.svg} (95%) rename dev/examples/wheel/{db3c5150.svg => d49d9ee8.svg} (89%) rename dev/trajectory_planning/{f393e660.svg => 1309f726.svg} (97%) rename dev/trajectory_planning/{919aef15.svg => cb8d3622.svg} (95%) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 2a04dd94..2207263c 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-10-15T10:50:35","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-10-15T11:38:49","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/dev/components/index.html b/dev/components/index.html index 2b4dcf12..7b8b0940 100644 --- a/dev/components/index.html +++ b/dev/components/index.html @@ -1,13 +1,13 @@ -Components · Multibody.jl

Components

The perhaps most fundamental component is a Body, this component has a single flange, frame_a, which is used to connect the body to other components. This component has a mass, a vector r_cm from frame_a to the center of mass, and a moment of inertia tensor I in the center of mass. The body can be thought of as a point mass with a moment of inertia tensor.

A mass with a shape can be modeled using a BodyShape. The primary difference between a Body and a BodyShape is that the latter has an additional flange, frame_b, which is used to connect the body to other components. The translation between flange_a and flange_b is determined by the vector r. The BodyShape is suitable to model, e.g., cylinders, rods, and boxes.

A rod without a mass (just a translation), is modeled using FixedTranslation.

Multibody.BodyBoxConstant
BodyBox(; name, m = 1, r = [1, 0, 0], r_shape = [0, 0, 0], width_dir = [0,1,0])

Rigid body with box shape. The mass properties of the body (mass, center of mass, inertia tensor) are computed from the box data. Optionally, the box may be hollow. The (outer) box shape is used in the animation, the hollow part is not shown in the animation. The two connector frames frame_a and frame_b are always parallel to each other.

Parameters

  • r: (structural parameter) Vector from frame_a to frame_b resolved in frame_a
  • r_shape: (structural parameter) Vector from frame_a to box origin, resolved in frame_a
  • width_dir: (structural parameter) Vector in width direction of box, resolved in frame_a
  • length_dir: (structural parameter) Vector in length direction of box, resolved in frame_a
  • length: (structural parameter) Length of box
  • width = 0.3length: Width of box
  • height = width: Height of box
  • inner_width: Width of inner box surface (0 <= inner_width <= width)
  • inner_height: Height of inner box surface (0 <= inner_height <= height)
  • density = 7700: Density of cylinder (e.g., steel: 7700 .. 7900, wood : 400 .. 800)
  • color: Color of box in animations
source
Multibody.BodyCylinderConstant
BodyCylinder(; name, m = 1, r = [0.1, 0, 0], r_shape = [0, 0, 0], dir = r - r_shape, length = _norm(r - r_shape), diameter = 1, inner_diameter = 0, density = 7700, color = purple)

Rigid body with cylinder shape. The mass properties of the body (mass, center of mass, inertia tensor) are computed from the cylinder data. Optionally, the cylinder may be hollow. The two connector frames frame_a and frame_b are always parallel to each other.

Parameters

  • r: (Structural parameter) Vector from frame_a to frame_b resolved in frame_a
  • r_shape: (Structural parameter) Vector from frame_a to cylinder origin, resolved in frame_a
  • dir: Vector in length direction of cylinder, resolved in frame_a
  • length: Length of cylinder
  • diameter: Diameter of cylinder
  • inner_diameter: Inner diameter of cylinder (0 <= inner_diameter <= diameter)
  • density: Density of cylinder kg/m³
  • color: Color of cylinder in animations

Variables

  • r_0: Position vector from origin of world frame to origin of frame_a
  • v_0: Absolute velocity of frame_a, resolved in world frame (= D(r_0))
  • a_0: Absolute acceleration of frame_a resolved in world frame (= D(v_0))
source
Multibody.worldConstant

The world component is the root of all multibody models. It is a fixed frame with a parallel gravitational field and a gravity vector specified by the unit direction world.n (defaults to [0, -1, 0]) and magnitude world.g (defaults to 9.80665).

source
Multibody.AccSensorMethod
AccSensor(;name)

Ideal rotational sensor to measure the absolute flange angular acceleration

Connectors:

  • flange: Flange Flange of shaft from which sensor information shall be measured
  • a: RealOutput Absolute angular acceleration of flange
source
Multibody.AxisControlBusMethod
@connector AxisControlBus(; name)
  • motion_ref(t) = 0: = true, if reference motion is not in rest
  • angle_ref(t) = 0: Reference angle of axis flange
  • angle(t) = 0: Angle of axis flange
  • speed_ref(t) = 0: Reference speed of axis flange
  • speed(t) = 0: Speed of axis flange
  • acceleration_ref(t) = 0: Reference acceleration of axis flange
  • acceleration(t) = 0: Acceleration of axis flange
  • current_ref(t) = 0: Reference current of motor
  • current(t) = 0: Current of motor
  • motorAngle(t) = 0: Angle of motor flange
  • motorSpeed(t) = 0: Speed of motor flange
source
Multibody.BodyMethod
Body(; name, m = 1, r_cm, isroot = false, phi0 = zeros(3), phid0 = zeros(3), r_0 = zeros(3), state_priority = 2, quat = false, sparse_I = false)

Representing a body with 3 translational and 3 rotational degrees-of-freedom.

This component has a single frame, frame_a. To represent bodies with more than one frame, see BodyShape, BodyCylinder, BodyBox. The inertia tensor is defined with respect to a coordinate system that is parallel to frame_a with the origin at the center of mass of the body.

Performance optimization

  • sparse_I: If true, the zero elements of the inerita matrix are considered "structurally zero", and this fact is used to optimize performance. When this option is enabled, the elements of the inertia matrix that were zero when the component was created cannot changed without reinstantiating the component. This performance optimization may be useful, e.g., when the inertia matrix is known to be diagonal.

Parameters

  • m: Mass
  • r_cm: Vector from frame_a to center of mass, resolved in frame_a
  • I_11, I_22, I_33, I_21, I_31, I_32: Inertia-matrix elements
  • isroot: Indicate whether this component is the root of the system, useful when there are no joints in the model.
  • phi0: Initial orientation, only applicable if isroot = true and quat = false
  • phid0: Initial angular velocity

Variables

  • r_0: Position vector from origin of world frame to origin of frame_a
  • v_0: Absolute velocity of frame_a, resolved in world frame (= D(r_0))
  • a_0: Absolute acceleration of frame_a resolved in world frame (= D(v_0))

Rendering options

  • radius: Radius of the joint in animations
  • cylinder_radius: Radius of the cylinder from frame to COM in animations (only drawn if r_cm is non-zero). Defaults to radius/2
  • color: Color of the joint in animations, a vector of length 4 with values between [0, 1] providing RGBA values
source
Multibody.BodyShapeMethod
BodyShape(; name, m = 1, r, kwargs...)

The BodyShape component is similar to a Body, but it has two frames and a vector r that describes the translation between them, while the body has a single frame only.

  • r: Vector from frame_a to frame_b resolved in frame_a
  • All kwargs are passed to the internal Body component.
  • shapefile: A path::String to a CAD model that can be imported by MeshIO for 3D rendering. If none is provided, a cylinder shape is rendered.

See also BodyCylinder and BodyBox for body components with predefined shapes and automatically computed inertial properties based on geometry and density.

source
Multibody.FixedMethod
Fixed(; name, r = [0, 0, 0], render = true)

A component rigidly attached to the world frame with translation r between the world and the frame_b. The position vector r is resolved in the world frame.

source
Multibody.FixedRotationMethod
FixedRotation(; name, r, n, sequence, isroot = false, angle)

Fixed translation followed by a fixed rotation of frame_b with respect to frame_a

  • r: Translation vector
  • n: Axis of rotation, resolved in frame_a
  • angle: Angle of rotation around n, given in radians

To obtain an axis-angle representation of any rotation, see Conversion between orientation formats

source
Multibody.PoseMethod
Pose(; name, r = [0, 0, 0], R, q, render = true)

Forced movement of a flange according to a reference position r and reference orientation R. The reference arrays r and R are resolved in the world frame, and may be any symbolic expression. As an alternative to specifying R, it is possible to specify a quaternion q (4-vector quaternion with real part first).

Example usage:

using Multibody.Rotations
+Components · Multibody.jl

Components

The perhaps most fundamental component is a Body, this component has a single flange, frame_a, which is used to connect the body to other components. This component has a mass, a vector r_cm from frame_a to the center of mass, and a moment of inertia tensor I in the center of mass. The body can be thought of as a point mass with a moment of inertia tensor.

A mass with a shape can be modeled using a BodyShape. The primary difference between a Body and a BodyShape is that the latter has an additional flange, frame_b, which is used to connect the body to other components. The translation between flange_a and flange_b is determined by the vector r. The BodyShape is suitable to model, e.g., cylinders, rods, and boxes.

A rod without a mass (just a translation), is modeled using FixedTranslation.

Multibody.BodyBoxConstant
BodyBox(; name, m = 1, r = [1, 0, 0], r_shape = [0, 0, 0], width_dir = [0,1,0])

Rigid body with box shape. The mass properties of the body (mass, center of mass, inertia tensor) are computed from the box data. Optionally, the box may be hollow. The (outer) box shape is used in the animation, the hollow part is not shown in the animation. The two connector frames frame_a and frame_b are always parallel to each other.

Parameters

  • r: (structural parameter) Vector from frame_a to frame_b resolved in frame_a
  • r_shape: (structural parameter) Vector from frame_a to box origin, resolved in frame_a
  • width_dir: (structural parameter) Vector in width direction of box, resolved in frame_a
  • length_dir: (structural parameter) Vector in length direction of box, resolved in frame_a
  • length: (structural parameter) Length of box
  • width = 0.3length: Width of box
  • height = width: Height of box
  • inner_width: Width of inner box surface (0 <= inner_width <= width)
  • inner_height: Height of inner box surface (0 <= inner_height <= height)
  • density = 7700: Density of cylinder (e.g., steel: 7700 .. 7900, wood : 400 .. 800)
  • color: Color of box in animations
source
Multibody.BodyCylinderConstant
BodyCylinder(; name, m = 1, r = [0.1, 0, 0], r_shape = [0, 0, 0], dir = r - r_shape, length = _norm(r - r_shape), diameter = 1, inner_diameter = 0, density = 7700, color = purple)

Rigid body with cylinder shape. The mass properties of the body (mass, center of mass, inertia tensor) are computed from the cylinder data. Optionally, the cylinder may be hollow. The two connector frames frame_a and frame_b are always parallel to each other.

Parameters

  • r: (Structural parameter) Vector from frame_a to frame_b resolved in frame_a
  • r_shape: (Structural parameter) Vector from frame_a to cylinder origin, resolved in frame_a
  • dir: Vector in length direction of cylinder, resolved in frame_a
  • length: Length of cylinder
  • diameter: Diameter of cylinder
  • inner_diameter: Inner diameter of cylinder (0 <= inner_diameter <= diameter)
  • density: Density of cylinder kg/m³
  • color: Color of cylinder in animations

Variables

  • r_0: Position vector from origin of world frame to origin of frame_a
  • v_0: Absolute velocity of frame_a, resolved in world frame (= D(r_0))
  • a_0: Absolute acceleration of frame_a resolved in world frame (= D(v_0))
source
Multibody.worldConstant

The world component is the root of all multibody models. It is a fixed frame with a parallel gravitational field and a gravity vector specified by the unit direction world.n (defaults to [0, -1, 0]) and magnitude world.g (defaults to 9.80665).

source
Multibody.AccSensorMethod
AccSensor(;name)

Ideal rotational sensor to measure the absolute flange angular acceleration

Connectors:

  • flange: Flange Flange of shaft from which sensor information shall be measured
  • a: RealOutput Absolute angular acceleration of flange
source
Multibody.AxisControlBusMethod
@connector AxisControlBus(; name)
  • motion_ref(t) = 0: = true, if reference motion is not in rest
  • angle_ref(t) = 0: Reference angle of axis flange
  • angle(t) = 0: Angle of axis flange
  • speed_ref(t) = 0: Reference speed of axis flange
  • speed(t) = 0: Speed of axis flange
  • acceleration_ref(t) = 0: Reference acceleration of axis flange
  • acceleration(t) = 0: Acceleration of axis flange
  • current_ref(t) = 0: Reference current of motor
  • current(t) = 0: Current of motor
  • motorAngle(t) = 0: Angle of motor flange
  • motorSpeed(t) = 0: Speed of motor flange
source
Multibody.BodyMethod
Body(; name, m = 1, r_cm, isroot = false, phi0 = zeros(3), phid0 = zeros(3), r_0 = zeros(3), state_priority = 2, quat = false, sparse_I = false)

Representing a body with 3 translational and 3 rotational degrees-of-freedom.

This component has a single frame, frame_a. To represent bodies with more than one frame, see BodyShape, BodyCylinder, BodyBox. The inertia tensor is defined with respect to a coordinate system that is parallel to frame_a with the origin at the center of mass of the body.

Performance optimization

  • sparse_I: If true, the zero elements of the inerita matrix are considered "structurally zero", and this fact is used to optimize performance. When this option is enabled, the elements of the inertia matrix that were zero when the component was created cannot changed without reinstantiating the component. This performance optimization may be useful, e.g., when the inertia matrix is known to be diagonal.

Parameters

  • m: Mass
  • r_cm: Vector from frame_a to center of mass, resolved in frame_a
  • I_11, I_22, I_33, I_21, I_31, I_32: Inertia-matrix elements
  • isroot: Indicate whether this component is the root of the system, useful when there are no joints in the model.
  • phi0: Initial orientation, only applicable if isroot = true and quat = false
  • phid0: Initial angular velocity

Variables

  • r_0: Position vector from origin of world frame to origin of frame_a
  • v_0: Absolute velocity of frame_a, resolved in world frame (= D(r_0))
  • a_0: Absolute acceleration of frame_a resolved in world frame (= D(v_0))

Rendering options

  • radius: Radius of the joint in animations
  • cylinder_radius: Radius of the cylinder from frame to COM in animations (only drawn if r_cm is non-zero). Defaults to radius/2
  • color: Color of the joint in animations, a vector of length 4 with values between [0, 1] providing RGBA values
source
Multibody.BodyShapeMethod
BodyShape(; name, m = 1, r, kwargs...)

The BodyShape component is similar to a Body, but it has two frames and a vector r that describes the translation between them, while the body has a single frame only.

  • r: Vector from frame_a to frame_b resolved in frame_a
  • All kwargs are passed to the internal Body component.
  • shapefile: A path::String to a CAD model that can be imported by MeshIO for 3D rendering. If none is provided, a cylinder shape is rendered.

See also BodyCylinder and BodyBox for body components with predefined shapes and automatically computed inertial properties based on geometry and density.

source
Multibody.FixedMethod
Fixed(; name, r = [0, 0, 0], render = true)

A component rigidly attached to the world frame with translation r between the world and the frame_b. The position vector r is resolved in the world frame.

source
Multibody.FixedRotationMethod
FixedRotation(; name, r, n, sequence, isroot = false, angle)

Fixed translation followed by a fixed rotation of frame_b with respect to frame_a

  • r: Translation vector
  • n: Axis of rotation, resolved in frame_a
  • angle: Angle of rotation around n, given in radians

To obtain an axis-angle representation of any rotation, see Conversion between orientation formats

source
Multibody.PoseMethod
Pose(; name, r = [0, 0, 0], R, q, render = true)

Forced movement of a flange according to a reference position r and reference orientation R. The reference arrays r and R are resolved in the world frame, and may be any symbolic expression. As an alternative to specifying R, it is possible to specify a quaternion q (4-vector quaternion with real part first).

Example usage:

using Multibody.Rotations
 R = RotXYZ(0, 0.5sin(t), 0)
-@named rot = Multibody.Pose(; r=[sin(t), 0, 0], R)

Connectors

  • frame_b: The frame that is forced to move according to the reference position and orientation.

Arguments

  • r: Position vector from world frame to frame_b, resolved in world frame
  • R: Reference orientation matrix
  • q: Reference quaternion (optional alternative to R)
  • render: Render the component in animations
  • normalize: If a quaternion is provided, normalize the quaternion (default = true)
  • x_locked, y_locked, z_locked: Lock the translation in the x, y, and z directions, respectively. This allows for selective specification of the translation components, i.e., if y_locked = false, the y-component of the translation is not constrained to follow r.

See also Position for a component that allows for only forced translation.

source
Multibody.PositionMethod
Position(; name, r = [0, 0, 0], render = true, fixed_orientation = true)

Forced movement of a flange according to a reference position r. Similar to Fixed, but r is not a parameter, and may thus be any time-varying symbolic expression. The reference position vector r is resolved in the world frame. Example: r = [sin(t), 0, 0].

Arguments:

  • r: Position vector from world frame to frame_b, resolved in world frame
  • render: Render the component in animations
  • fixed_orientation: If true, the orientation of the frame is fixed to the world frame. If false, the orientation is free to change.

See also Pose for a component that allows for forced orientation as well.

source
Multibody.RopeMethod
Rope(; name, l = 1, n = 10, m = 1, c = 0, d = 0, kwargs)

Model a rope (string / cable) of length l and mass m.

The rope is modeled as a series of n links, each connected by a Spherical joint. The links are either fixed in length (default, modeled using BodyShape) or flexible, in which case they are modeled as a Translational.Spring and Translational.Damper in parallel with a Prismatic joint with a Body adding mass to the center of the link segment. The flexibility is controlled by the parameters c and d, which are the stiffness and damping coefficients of the spring and damper, respectively. The default values are c = 0 and d = 0, which corresponds to a stiff rope.

  • l: Unstretched length of rope
  • n: Number of links used to model the rope. For accurate approximations to continuously flexible ropes, a large number may be required.
  • m: The total mass of the rope. Each rope segment will have mass m / n.
  • c: The equivalent stiffness of the rope, i.e., the rope will act like a spring with stiffness c.
  • d: The equivalent damping in the stretching direction of the rope, i.e., the taught rope will act like a damper with damping d.
  • d_joint: Viscous damping in the joints between the links. A positive value makes the rope dissipate energy while flexing (as opposed to the damping d which dissipats energy due to stretching).
  • dir: A vector of norm 1 indicating the initial direction of the rope.

Damping

There are three different methods of adding damping to the rope:

  • Damping in the stretching direction of the rope, controlled by the parameter d.
  • Damping in flexing of the rope, modeled as viscous friction in the joints between the links, controlled by the parameter d_joint.
  • Air resistance to the rope moving through the air, controlled by the parameter air_resistance. This damping is quadratic in the velocity ($f_d ~ -||v||v$) of each link relative to the world frame.

Rendering

  • color = [255, 219, 120, 255]./255
  • radius = 0.05f0
  • jointradius=0
  • jointcolor=color
source
Multibody.WorldMethod
World(; name, render=true, point_gravity=false, n = [0.0, -1.0, 0.0], g=9.80665, mu=3.986004418e14)

All multibody models must include exactly one world component defined at the top level. The frame_b of the world is fixed in the origin.

If a connection to the world is needed in a component model, use Fixed instead.

Arguments

  • name: Name of the world component
  • render: Render the component in animations
  • point_gravity: If true, the gravity is always opinting towards a single point in space. If false, the gravity is always pointing in the same direction n.
  • n: Gravity direction unit vector, defaults to [0, -1, 0], only applicable if point_gravity = false
  • g: Gravitational acceleration, defaults to 9.80665
  • mu: Gravity field constant, defaults to 3.986004418e14, only applicable to point gravity
source
Multibody.oriFunction
ori(frame, varw = false)

Get the orientation of sys as a RotationMatrix object. See also get_rot. ori(frame).R is the rotation matrix that rotates a vector from the world coordinate system to the local frame.

For frames, the orientation is stored in the metadata field of the system as get_metadata(sys)[:orientation].

If varw = true, the angular velocity variables w of the frame is also included in the RotationMatrix object, otherwise w is derived as the time derivative of R. varw = true is primarily used when selecting a component as root.

source
Multibody.RollingConstraintVerticalWheelMethod
RollingConstraintVerticalWheel(;
+@named rot = Multibody.Pose(; r=[sin(t), 0, 0], R)

Connectors

  • frame_b: The frame that is forced to move according to the reference position and orientation.

Arguments

  • r: Position vector from world frame to frame_b, resolved in world frame
  • R: Reference orientation matrix
  • q: Reference quaternion (optional alternative to R)
  • render: Render the component in animations
  • normalize: If a quaternion is provided, normalize the quaternion (default = true)
  • x_locked, y_locked, z_locked: Lock the translation in the x, y, and z directions, respectively. This allows for selective specification of the translation components, i.e., if y_locked = false, the y-component of the translation is not constrained to follow r.

See also Position for a component that allows for only forced translation.

source
Multibody.PositionMethod
Position(; name, r = [0, 0, 0], render = true, fixed_orientation = true)

Forced movement of a flange according to a reference position r. Similar to Fixed, but r is not a parameter, and may thus be any time-varying symbolic expression. The reference position vector r is resolved in the world frame. Example: r = [sin(t), 0, 0].

Arguments:

  • r: Position vector from world frame to frame_b, resolved in world frame
  • render: Render the component in animations
  • fixed_orientation: If true, the orientation of the frame is fixed to the world frame. If false, the orientation is free to change.

See also Pose for a component that allows for forced orientation as well.

source
Multibody.RopeMethod
Rope(; name, l = 1, n = 10, m = 1, c = 0, d = 0, kwargs)

Model a rope (string / cable) of length l and mass m.

The rope is modeled as a series of n links, each connected by a Spherical joint. The links are either fixed in length (default, modeled using BodyShape) or flexible, in which case they are modeled as a Translational.Spring and Translational.Damper in parallel with a Prismatic joint with a Body adding mass to the center of the link segment. The flexibility is controlled by the parameters c and d, which are the stiffness and damping coefficients of the spring and damper, respectively. The default values are c = 0 and d = 0, which corresponds to a stiff rope.

  • l: Unstretched length of rope
  • n: Number of links used to model the rope. For accurate approximations to continuously flexible ropes, a large number may be required.
  • m: The total mass of the rope. Each rope segment will have mass m / n.
  • c: The equivalent stiffness of the rope, i.e., the rope will act like a spring with stiffness c.
  • d: The equivalent damping in the stretching direction of the rope, i.e., the taught rope will act like a damper with damping d.
  • d_joint: Viscous damping in the joints between the links. A positive value makes the rope dissipate energy while flexing (as opposed to the damping d which dissipats energy due to stretching).
  • dir: A vector of norm 1 indicating the initial direction of the rope.

Damping

There are three different methods of adding damping to the rope:

  • Damping in the stretching direction of the rope, controlled by the parameter d.
  • Damping in flexing of the rope, modeled as viscous friction in the joints between the links, controlled by the parameter d_joint.
  • Air resistance to the rope moving through the air, controlled by the parameter air_resistance. This damping is quadratic in the velocity ($f_d ~ -||v||v$) of each link relative to the world frame.

Rendering

  • color = [255, 219, 120, 255]./255
  • radius = 0.05f0
  • jointradius=0
  • jointcolor=color
source
Multibody.WorldMethod
World(; name, render=true, point_gravity=false, n = [0.0, -1.0, 0.0], g=9.80665, mu=3.986004418e14)

All multibody models must include exactly one world component defined at the top level. The frame_b of the world is fixed in the origin.

If a connection to the world is needed in a component model, use Fixed instead.

Arguments

  • name: Name of the world component
  • render: Render the component in animations
  • point_gravity: If true, the gravity is always opinting towards a single point in space. If false, the gravity is always pointing in the same direction n.
  • n: Gravity direction unit vector, defaults to [0, -1, 0], only applicable if point_gravity = false
  • g: Gravitational acceleration, defaults to 9.80665
  • mu: Gravity field constant, defaults to 3.986004418e14, only applicable to point gravity
source
Multibody.oriFunction
ori(frame, varw = false)

Get the orientation of sys as a RotationMatrix object. See also get_rot. ori(frame).R is the rotation matrix that rotates a vector from the world coordinate system to the local frame.

For frames, the orientation is stored in the metadata field of the system as get_metadata(sys)[:orientation].

If varw = true, the angular velocity variables w of the frame is also included in the RotationMatrix object, otherwise w is derived as the time derivative of R. varw = true is primarily used when selecting a component as root.

source
Multibody.RollingConstraintVerticalWheelMethod
RollingConstraintVerticalWheel(;
     name,
     radius = 0.3,
     lateral_sliding_constraint = true,
-)

Rolling constraint for wheel that is always perpendicular to x-z plane

Joint for a wheel rolling on the x-z plane of the world frame intended for an idealized wheelset. To meet this objective, the wheel always runs upright and enables no slip in the longitudinal direction of the wheel/ground contact. On the contrary, the wheel can optionally slip in the lateral direction which is reasonable for the wheelset where just one of the wheels should be laterally constrained. The frame frame_a is placed in the intersection of the wheel spin axis with the wheel middle plane and rotates with the wheel itself. A wheel body collecting the mass and inertia

Arguments and parameters:

  • name: Name of the rolling wheel joint component
  • radius: Wheel radius
  • lateral_sliding_constraint: true, if lateral sliding constraint taken into account, = false if lateral force = 0 (needed to avoid overconstraining if two ideal rolling wheels are connect on one axis)

Connectors:

  • frame_a: Frame for the wheel joint
source
Multibody.RollingWheelMethod
RollingWheel(; name, radius, m, I_axis, I_long, width=0.035, x0, y0, kwargs...)

Ideal rolling wheel on flat surface y=0 (5 positional, 3 velocity degrees of freedom)

A wheel rolling on the x-z plane of the world frame including wheel mass. The rolling contact is considered being ideal, i.e. there is no slip between the wheel and the ground. The wheel can not take off but it can incline toward the ground. The frame frame_a is placed in the wheel center point and rotates with the wheel itself. A Revolute joint rotationg around n = [0, 1, 0] is required to attach the wheel to a wheel axis.

Arguments and parameters:

  • name: Name of the rolling wheel component
  • radius: Radius of the wheel
  • m: Mass of the wheel
  • I_axis: Moment of inertia of the wheel along its axis
  • I_long: Moment of inertia of the wheel perpendicular to its axis
  • width: Width of the wheel (default: 0.035)
  • x0: Initial x-position of the wheel axis
  • z0: Initial z-position of the wheel axis
  • kwargs...: Additional keyword arguments passed to the RollingWheelJoint function

Variables:

  • x: x-position of the wheel axis
  • z: z-position of the wheel axis
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • der_angles: Derivatives of angles (y: like rotational velocity of a spinning coin, z: wheel forward spin speed, x: wheel falling over speed)

Named components:

  • frame_a: Frame for the wheel component
  • wheeljoint: Rolling wheel joint representing the wheel's contact with the road surface
Rendering tip

Due to the symmetry of the wheel, it can be hard to discern how the wheel is rotating in animations. Try enabling rendering of the frame of the wheel by setting

wheel.frame_a.render => true;
+)

Rolling constraint for wheel that is always perpendicular to x-z plane

Joint for a wheel rolling on the x-z plane of the world frame intended for an idealized wheelset. To meet this objective, the wheel always runs upright and enables no slip in the longitudinal direction of the wheel/ground contact. On the contrary, the wheel can optionally slip in the lateral direction which is reasonable for the wheelset where just one of the wheels should be laterally constrained. The frame frame_a is placed in the intersection of the wheel spin axis with the wheel middle plane and rotates with the wheel itself. A wheel body collecting the mass and inertia

Arguments and parameters:

  • name: Name of the rolling wheel joint component
  • radius: Wheel radius
  • lateral_sliding_constraint: true, if lateral sliding constraint taken into account, = false if lateral force = 0 (needed to avoid overconstraining if two ideal rolling wheels are connect on one axis)

Connectors:

  • frame_a: Frame for the wheel joint
source
Multibody.RollingWheelMethod
RollingWheel(; name, radius, m, I_axis, I_long, width=0.035, x0, y0, kwargs...)

Ideal rolling wheel on flat surface y=0 (5 positional, 3 velocity degrees of freedom)

A wheel rolling on the x-z plane of the world frame including wheel mass. The rolling contact is considered being ideal, i.e. there is no slip between the wheel and the ground. The wheel can not take off but it can incline toward the ground. The frame frame_a is placed in the wheel center point and rotates with the wheel itself. A Revolute joint rotationg around n = [0, 1, 0] is required to attach the wheel to a wheel axis.

Arguments and parameters:

  • name: Name of the rolling wheel component
  • radius: Radius of the wheel
  • m: Mass of the wheel
  • I_axis: Moment of inertia of the wheel along its axis
  • I_long: Moment of inertia of the wheel perpendicular to its axis
  • width: Width of the wheel (default: 0.035)
  • x0: Initial x-position of the wheel axis
  • z0: Initial z-position of the wheel axis
  • kwargs...: Additional keyword arguments passed to the RollingWheelJoint function

Variables:

  • x: x-position of the wheel axis
  • z: z-position of the wheel axis
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • der_angles: Derivatives of angles (y: like rotational velocity of a spinning coin, z: wheel forward spin speed, x: wheel falling over speed)

Named components:

  • frame_a: Frame for the wheel component
  • wheeljoint: Rolling wheel joint representing the wheel's contact with the road surface
Rendering tip

Due to the symmetry of the wheel, it can be hard to discern how the wheel is rotating in animations. Try enabling rendering of the frame of the wheel by setting

wheel.frame_a.render => true;
 wheel.frame_a.length => 1.1radius;
-wheel.frame_a.radius => 0.02radius;
source
Multibody.RollingWheelJointMethod
RollingWheelJoint(; name, radius, angles, x0, y0, z0)

Joint (no mass, no inertia) that describes an ideal rolling wheel (rolling on the plane y=0). See RollingWheel for a realistic wheel model with inertia.

A joint for a wheel rolling on the x-z plane of the world frame. The rolling contact is considered being ideal, i.e. there is no slip between the wheel and the ground. This is simply gained by two non-holonomic constraint equations on velocity level defined for both longitudinal and lateral direction of the wheel. There is also a holonomic constraint equation on position level granting a permanent contact of the wheel to the ground, i.e. the wheel can not take off.

The origin of the frame frame_a is placed in the intersection of the wheel spin axis with the wheel middle plane and rotates with the wheel itself. The z-axis of frame_a is identical with the wheel spin axis, i.e. the wheel rotates about z-axis of frame_a. A wheel body collecting the mass and inertia should be connected to this frame.

Arguments and parameters:

  • radius: Radius of the wheel
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • surface: By default, the wheel is rolling on a flat xz plane. A function surface = (x, z)->y may be provided to define a road surface. The function should return the height of the road at (x, z).

Variables:

  • x: x-position of the wheel axis
  • y: y-position of the wheel axis
  • z: z-position of the wheel axis
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • der_angles: Derivatives of angles
  • r_road_0: Position vector from world frame to contact point on road, resolved in world frame
  • f_wheel_0: Force vector on wheel, resolved in world frame
  • f_n: Contact force acting on wheel in normal direction
  • f_lat: Contact force acting on wheel in lateral direction
  • f_long: Contact force acting on wheel in longitudinal direction
  • err: Absolute value of (r_road_0 - frame_a.r_0) - radius (must be zero; used for checking)
  • e_axis_0: Unit vector along wheel axis, resolved in world frame
  • delta_0: Distance vector from wheel center to contact point
  • e_n_0: Unit vector in normal direction of road at contact point, resolved in world frame
  • e_lat_0: Unit vector in lateral direction of road at contact point, resolved in world frame
  • e_long_0: Unit vector in longitudinal direction of road at contact point, resolved in world frame
  • s: Road surface parameter 1
  • w: Road surface parameter 2
  • e_s_0: Road heading at (s,w), resolved in world frame (unit vector)
  • v_0: Velocity of wheel center, resolved in world frame
  • w_0: Angular velocity of wheel, resolved in world frame
  • vContact_0: Velocity of contact point, resolved in world frame

Connector frames

  • frame_a: Frame for the wheel joint
source
source
Multibody.RollingWheelJointMethod
RollingWheelJoint(; name, radius, angles, x0, y0, z0)

Joint (no mass, no inertia) that describes an ideal rolling wheel (rolling on the plane y=0). See RollingWheel for a realistic wheel model with inertia.

A joint for a wheel rolling on the x-z plane of the world frame. The rolling contact is considered being ideal, i.e. there is no slip between the wheel and the ground. This is simply gained by two non-holonomic constraint equations on velocity level defined for both longitudinal and lateral direction of the wheel. There is also a holonomic constraint equation on position level granting a permanent contact of the wheel to the ground, i.e. the wheel can not take off.

The origin of the frame frame_a is placed in the intersection of the wheel spin axis with the wheel middle plane and rotates with the wheel itself. The z-axis of frame_a is identical with the wheel spin axis, i.e. the wheel rotates about z-axis of frame_a. A wheel body collecting the mass and inertia should be connected to this frame.

Arguments and parameters:

  • radius: Radius of the wheel
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • surface: By default, the wheel is rolling on a flat xz plane. A function surface = (x, z)->y may be provided to define a road surface. The function should return the height of the road at (x, z).

Variables:

  • x: x-position of the wheel axis
  • y: y-position of the wheel axis
  • z: z-position of the wheel axis
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • der_angles: Derivatives of angles
  • r_road_0: Position vector from world frame to contact point on road, resolved in world frame
  • f_wheel_0: Force vector on wheel, resolved in world frame
  • f_n: Contact force acting on wheel in normal direction
  • f_lat: Contact force acting on wheel in lateral direction
  • f_long: Contact force acting on wheel in longitudinal direction
  • err: Absolute value of (r_road_0 - frame_a.r_0) - radius (must be zero; used for checking)
  • e_axis_0: Unit vector along wheel axis, resolved in world frame
  • delta_0: Distance vector from wheel center to contact point
  • e_n_0: Unit vector in normal direction of road at contact point, resolved in world frame
  • e_lat_0: Unit vector in lateral direction of road at contact point, resolved in world frame
  • e_long_0: Unit vector in longitudinal direction of road at contact point, resolved in world frame
  • s: Road surface parameter 1
  • w: Road surface parameter 2
  • e_s_0: Road heading at (s,w), resolved in world frame (unit vector)
  • v_0: Velocity of wheel center, resolved in world frame
  • w_0: Angular velocity of wheel, resolved in world frame
  • vContact_0: Velocity of contact point, resolved in world frame

Connector frames

  • frame_a: Frame for the wheel joint
source
Multibody.RollingWheelSetMethod
RollingWheelSet(;
     name,
     radius = 0.3,
     m_wheel = 1.0,
@@ -26,7 +26,7 @@
     color = [0.3, 0.3, 0.3, 1],
     render = true,
     iscut = false,
-)

Ideal rolling wheel set consisting of two ideal rolling wheels connected together by an axis

Two wheels are connected by an axis and can rotate around this axis. The wheels are rolling on the x-z plane of the world frame. The coordinate system attached to the center of the wheel axis (frame_middle) is constrained so that it is always parallel to the x-z plane. If all generalized coordinates are zero, frame_middle is parallel to the world frame.

Arguments and parameters:

  • iscut: if more than one wheel set is connected to the same rigid body, iscut must be set to true for all but one set. This avoids overconstraining the system by replacing the planar joint giving the set coordinates by an unconstrained FreeMotion joint.
  • radius: Radius of one wheel
  • m_wheel: Mass of one wheel
  • I_axis: Moment of inertia of one wheel around its rotation axis
  • I_long: Moment of inertia of one wheel perpendicular to its rotation axis
  • track: Distance between the two wheels (= axle track)

Connectors

  • frame_middle: Frame fixed in middle of axis connecting both wheels (z-axis: along wheel axis, y-axis: upwards)
  • frame1: Frame fixed in center point of left wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • frame2: Frame fixed in center point of right wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • axis1: 1-dim. Rotational flange that drives the left wheel
  • axis2: 1-dim. Rotational flange that drives the right wheel
  • support: Support of 1D axes

To connect driving torques or friction to rotation of the wheels, connect between axis1 and support, and axis2 and support respectively. To connect the wheel set to, e.g., a body, connect the frame_middle to the body frame.

source
Multibody.RollingWheelSetJointMethod
RollingWheelSetJoint(;
+)

Ideal rolling wheel set consisting of two ideal rolling wheels connected together by an axis

Two wheels are connected by an axis and can rotate around this axis. The wheels are rolling on the x-z plane of the world frame. The coordinate system attached to the center of the wheel axis (frame_middle) is constrained so that it is always parallel to the x-z plane. If all generalized coordinates are zero, frame_middle is parallel to the world frame.

Arguments and parameters:

  • iscut: if more than one wheel set is connected to the same rigid body, iscut must be set to true for all but one set. This avoids overconstraining the system by replacing the planar joint giving the set coordinates by an unconstrained FreeMotion joint.
  • radius: Radius of one wheel
  • m_wheel: Mass of one wheel
  • I_axis: Moment of inertia of one wheel around its rotation axis
  • I_long: Moment of inertia of one wheel perpendicular to its rotation axis
  • track: Distance between the two wheels (= axle track)

Connectors

  • frame_middle: Frame fixed in middle of axis connecting both wheels (z-axis: along wheel axis, y-axis: upwards)
  • frame1: Frame fixed in center point of left wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • frame2: Frame fixed in center point of right wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • axis1: 1-dim. Rotational flange that drives the left wheel
  • axis2: 1-dim. Rotational flange that drives the right wheel
  • support: Support of 1D axes

To connect driving torques or friction to rotation of the wheels, connect between axis1 and support, and axis2 and support respectively. To connect the wheel set to, e.g., a body, connect the frame_middle to the body frame.

source
Multibody.RollingWheelSetJointMethod
RollingWheelSetJoint(;
     name,
     radius = 0.3,
     track = 1.0,
@@ -40,7 +40,7 @@
     der_theta2_0 = 0,
     render = true,
     iscut = false,
-)

Joint (no mass, no inertia) that describes an ideal rolling wheel set (two ideal rolling wheels connected together by an axis)

An assembly joint for a wheelset rolling on the x-z plane of the world frame. The frames frame1 and frame2 are connected to rotating wheels; the frame_middle moves in a plane parallel to the x-z plane of the world and should be connected to the vehicle body.

To work properly, the gravity acceleration vector g of the world must point in the negative y-axis (default)

Arguments and parameters:

  • iscut: if more than one wheel set is connected to the same rigid body, iscut must be set to true for all but one set. This avoids overconstraining the system by replacing the planar joint giving the set coordinates by an unconstrained FreeMotion joint.
  • radius: Radius of one wheel
  • track: Distance between the two wheels (= axle track)

Connectors:

  • frame_middle: Frame fixed in middle of axis connecting both wheels (z-axis: along wheel axis, y-axis: upwards)
  • frame1: Frame fixed in center point of left wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • frame2: Frame fixed in center point of right wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • axis1: 1-dim. Rotational flange that drives the joint
  • axis2: 1-dim. Rotational flange that drives the joint
  • support: Support of 1-dim axes
source
Multibody.SlipWheelJointMethod
SlipWheelJoint(; name, radius, angles = zeros(3), der_angles = zeros(3), x0 = 0, y0 = radius, z0 = 0, sequence, iscut = false, surface = nothing, vAdhesion_min = 0.1, vSlide_min = 0.1, sAdhesion = 0.04, sSlide = 0.12, mu_A = 0.8, mu_S = 0.6, phi_roll = 0, w_roll = 0)

Joint for a wheel with slip rolling on a surface. See https://people.inf.ethz.ch/fcellier/MS/andres_ms.pdf for details.

Integrator choice

The slip model contains a discontinuity in the second derivative at the transitions between adhesion and sliding. This can cause problems for integrators, in particular BDF-type integrators.

Normal force

The wheel cannot leave the ground. Make sure that the normal force f_n never becomes negative.

Parameters

  • radius: Radius of the wheel
  • vAdhesion_min: Minimum velocity for the peak of the adhesion curve (regularization close to 0)
  • vSlide_min: Minimum velocity for the start of the flat region of the slip curve (regularization close to 0)
  • sAdhesion: Adhesion slippage. The peak of the adhesion curve appears when the wheel slip is equal to sAdhesion.
  • sSlide: Sliding slippage. The flat region of the adhesion curve appears when the wheel slip is greater than sSlide.
  • mu_A: Friction coefficient at adhesion
  • mu_S: Friction coefficient at sliding
  • surface: By default, the wheel is rolling on a flat xz plane. A function surface = (x, z)->y may be provided to define a road surface. The function should return the height of the road at (x, z). Note: if a function that depends on parameters is provided, make sure the parameters are scoped appropriately using, e.g., ParentScope.
  • state: (structural) whether or not the component has angular state variables. Default is true.

State and iscut

When the wheel is mounted on an axis that is rooted, one may either supply state=false or iscut = true. With state = false, the angular state variables are not included in the wheel and there is thus no kinematic chain introduced. This reduces the total number of variables in the system. if the angular variables are required, one may instead pass iscut=true to cut the kinematic loop that is introduced when coupling the angles of the wheel to the orientation of the frame_a, unless this is cut elsewhere.

Understaning the slip model

The following Julia code draws the slip model with descriptive labels

using Plots
+)

Joint (no mass, no inertia) that describes an ideal rolling wheel set (two ideal rolling wheels connected together by an axis)

An assembly joint for a wheelset rolling on the x-z plane of the world frame. The frames frame1 and frame2 are connected to rotating wheels; the frame_middle moves in a plane parallel to the x-z plane of the world and should be connected to the vehicle body.

To work properly, the gravity acceleration vector g of the world must point in the negative y-axis (default)

Arguments and parameters:

  • iscut: if more than one wheel set is connected to the same rigid body, iscut must be set to true for all but one set. This avoids overconstraining the system by replacing the planar joint giving the set coordinates by an unconstrained FreeMotion joint.
  • radius: Radius of one wheel
  • track: Distance between the two wheels (= axle track)

Connectors:

  • frame_middle: Frame fixed in middle of axis connecting both wheels (z-axis: along wheel axis, y-axis: upwards)
  • frame1: Frame fixed in center point of left wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • frame2: Frame fixed in center point of right wheel, rotating with the wheel (z-axis: along wheel axis, y-axis: upwards when wheel angle is zero)
  • axis1: 1-dim. Rotational flange that drives the joint
  • axis2: 1-dim. Rotational flange that drives the joint
  • support: Support of 1-dim axes
source
Multibody.SlipWheelJointMethod
SlipWheelJoint(; name, radius, angles = zeros(3), der_angles = zeros(3), x0 = 0, y0 = radius, z0 = 0, sequence, iscut = false, surface = nothing, vAdhesion_min = 0.1, vSlide_min = 0.1, sAdhesion = 0.04, sSlide = 0.12, mu_A = 0.8, mu_S = 0.6, phi_roll = 0, w_roll = 0)

Joint for a wheel with slip rolling on a surface. See https://people.inf.ethz.ch/fcellier/MS/andres_ms.pdf for details.

Integrator choice

The slip model contains a discontinuity in the second derivative at the transitions between adhesion and sliding. This can cause problems for integrators, in particular BDF-type integrators.

Normal force

The wheel cannot leave the ground. Make sure that the normal force f_n never becomes negative.

Parameters

  • radius: Radius of the wheel
  • vAdhesion_min: Minimum velocity for the peak of the adhesion curve (regularization close to 0)
  • vSlide_min: Minimum velocity for the start of the flat region of the slip curve (regularization close to 0)
  • sAdhesion: Adhesion slippage. The peak of the adhesion curve appears when the wheel slip is equal to sAdhesion.
  • sSlide: Sliding slippage. The flat region of the adhesion curve appears when the wheel slip is greater than sSlide.
  • mu_A: Friction coefficient at adhesion
  • mu_S: Friction coefficient at sliding
  • surface: By default, the wheel is rolling on a flat xz plane. A function surface = (x, z)->y may be provided to define a road surface. The function should return the height of the road at (x, z). Note: if a function that depends on parameters is provided, make sure the parameters are scoped appropriately using, e.g., ParentScope.
  • state: (structural) whether or not the component has angular state variables. Default is true.

State and iscut

When the wheel is mounted on an axis that is rooted, one may either supply state=false or iscut = true. With state = false, the angular state variables are not included in the wheel and there is thus no kinematic chain introduced. This reduces the total number of variables in the system. if the angular variables are required, one may instead pass iscut=true to cut the kinematic loop that is introduced when coupling the angles of the wheel to the orientation of the frame_a, unless this is cut elsewhere.

Understaning the slip model

The following Julia code draws the slip model with descriptive labels

using Plots
 vAdhesion = 0.2
 vSlide = 0.4
 mu_A = 0.95
@@ -56,8 +56,8 @@
     yticks = ((mu_A, mu_S), ["$\mu_{adhesion}$", "$\mu_{slide}$"]),
     framestyle = :zerolines,
     legend = false,
-)
source
Multibody.SlippingWheelMethod
SlippingWheel(; name, radius, m, I_axis, I_long, width = 0.035, x0=0, z0=0,
-                  angles = zeros(3), der_angles = zeros(3), kwargs...)

Wheel with slip rolling on a surface.

Parameters

  • radius: Radius of the wheel
  • m: Mass of the wheel
  • I_axis: Moment of inertia of the wheel along its axis
  • I_long: Moment of inertia of the wheel perpendicular to its axis
  • width: Width of the wheel (for rendering)
  • x0: Initial x-position of the wheel axis
  • z0: Initial z-position of the wheel axis
  • state: (structural) whether or not the component has angular state variables.

Variables

  • x: x-position of the wheel axis
  • z: z-position of the wheel axis
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • der_angles: Derivatives of angles

Connectors

  • frame_a: Frame for the wheel component

Examples

See Docs: Wheels

source
Multibody.PlanarMechanics.BodyShapeConstant
BodyShape(; name, r = [1,0], r_cm = 0.5*r, gy = -9.80665)

The BodyShape component is similar to a Body, but it has two frames and a vector r that describes the translation between them, while the body has a single frame only.

Parameters

  • r: (Structural) Vector from frame_a to frame_b resolved in frame_a
  • r_cm: (Structural) Vector from frame_a to the center of mass resolved in frame_a

Subsystems

  • translation: FixedTranslation Fixed translation between frame_a and frame_b
  • translation_cm: FixedTranslation Fixed translation between frame_a and the center of mass
  • body: Body Body component placed at center of mass. This component holds the inertial properties

Connectors

  • frame_a
  • frame_b
source
Multibody.PlanarMechanics.DamperConstant
Damper(; name, d = 1, s_small = 1.e-10)

Linear (velocity dependent) damper

Parameters:

  • d: [N.s/m] Damping constant
  • s_small: [m] Prevent zero-division if distance between framea and frameb is zero

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.DifferentialGearConstant
DifferentialGear(; name)

A 1D-rotational component that is a variant of a planetary gear and can be used to distribute the torque equally among the wheels on one axis.

Connectors:

  • flange_b (Rotational.Flange) Flange for the input torque
  • flange_left (Rotational.Flange) Flange for the left output torque
  • flange_right (Rotational.Flange) Flange for the right output torque
source
Multibody.PlanarMechanics.FixedConstant
Fixed(; name, r = [0.0, 0.0], phi = 0.0)

Frame fixed in the planar world frame at a given position and orientation

Parameters:

  • r: [m, m] Fixed absolute x,y-position, resolved in world frame
  • phi: [rad] Fixed angle

Connectors:

  • frame_b: 2-dim. Coordinate system
source
Multibody.PlanarMechanics.FixedTranslationConstant
FixedTranslation(; name, r::AbstractArray, l)

A fixed translation between two components (rigid rod)

Parameters:

  • rx: [m] Fixed x-length of the rod resolved w.r.t to body frame_a at phi = 0
  • ry: [m] Fixed y-length of the rod resolved w.r.t to body frame_a at phi = 0
  • radius: [m] Radius of the rod in animations
  • render: [Bool] Render the rod in animations

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.IdealPlanetaryConstant
IdealPlanetary(; name, ratio = 2)

The IdealPlanetary gear box is an ideal gear without inertia, elasticity, damping or backlash consisting of an inner sun wheel, an outer ring wheel and a planet wheel located between sun and ring wheel. The bearing of the planet wheel shaft is fixed in the planet carrier. The component can be connected to other elements at the sun, ring and/or carrier flanges. It is not possible to connect to the planet wheel. If inertia shall not be neglected, the sun, ring and carrier inertias can be easily added by attaching inertias (= model Inertia) to the corresponding connectors. The inertias of the planet wheels are always neglected.

The ideal planetary gearbox is uniquely defined by the ratio of the number of ring teeth $z_r$ with respect to the number of sun teeth $z_s$. For example, if there are 100 ring teeth and 50 sun teeth then ratio = $z_r/z_s = 2$. The number of planet teeth $z_p$ has to fulfill the following relationship:

\[z_p = (z_r - z_s) / 2\]

Therefore, in the above example $z_p = 25$ is required.

According to the overall convention, the positive direction of all vectors, especially the absolute angular velocities and cut-torques in the flanges, are along the axis vector displayed in the icon.

Parameters:

  • ratio: Number of ring teeth/sun teeth

Connectors:

  • sun (Rotational.Flange) Sun wheel
  • carrier (Rotational.Flange) Planet carrier
  • ring (Rotational.Flange) Ring wheel
source
Multibody.PlanarMechanics.SimpleWheelConstant
SimpleWheel(; name, radius = 0.3, color = [1, 0, 0, 1], μ = 1e9)

Simple wheel model with viscous lateral friction and a driving torque

Connectors:

  • frame_a (Frame) Coordinate system fixed to the component with one cut-force and cut-torque
  • thrust (RealInput) Input for the longitudinal force applied to the wheel

Parameters:

  • μ: [Ns/m] Viscous friction coefficient
  • radius: [m] Radius of the wheel
  • color: Color of the wheel in animations

Variables:

  • θ: [rad] Wheel angle
  • Vx: [m/s] Longitudinal velocity (resolved in local frame)
  • Vy: [m/s] Lateral velocity (resolved in local frame)
  • Fy: [N] Lateral friction force
  • Fx: [N] Applied longitudinal wheel force
source
Multibody.PlanarMechanics.SpringConstant
Spring(; name, c_x = 1, c_y = 1, c_phi = 1e5, s_relx0 = 0, s_rely0 = 0, phi_rel0 = 0, s_small = 1.e-10)

Linear 2D translational spring

Parameters:

  • c_x: [N/m] Spring constant in x dir
  • c_y: [N/m] Spring constant in y dir
  • c_phi: [N.m/rad] Spring constant in phi dir
  • s_relx0: [m] Unstretched spring length
  • s_rely0: [m] Unstretched spring length
  • phi_rel0: [rad] Unstretched spring angle
  • s_small: [m] Prevent zero-division if distance between framea and frameb is zero
  • num_windings: [Int] Number of windings of the coil when rendered
  • color = [0,0,1,1] Color of the spring in animations
  • render = true Render the spring in animations
  • radius = 0.1 Radius of spring when rendered
  • N = 200 Number of points in mesh when rendered

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.SpringDamperConstant
SpringDamper(; name, c_x = 1, c_y = 1, c_phi = 1e5, d_x = 1, d_y = 1, d_phi = 1, s_relx0 = 0, s_rely0 = 0, phi_rel0 = 0, s_small = 1.e-10)

Linear 2D translational spring damper model

Parameters:

  • c_x: [N/m] Spring constant in x dir
  • c_y: [N/m] Spring constant in y dir
  • c_phi: [N.m/rad] Spring constant in phi dir
  • d_x: [N.s/m] Damping constant in x dir
  • d_y: [N.s/m] Damping constant in y dir
  • d_phi: [N.m.s/rad] Damping constant in phi dir
  • s_relx0: [m] Unstretched spring length
  • s_rely0: [m] Unstretched spring length
  • phi_rel0: [rad] Unstretched spring angle
  • s_small: [m] Prevent zero-division if distance between framea and frameb is zero
  • num_windings: [Int] Number of windings of the coil when rendered
  • color = [0,0,1,1] Color of the spring in animations
  • render = true Render the spring in animations
  • radius = 0.1 Radius of spring when rendered
  • N = 200 Number of points in mesh when rendered

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.BodyMethod
Body(; name, m=1, I=0.1, r=0, gy=-9.80665, radius=0.1, render=true, color=Multibody.purple)

Body component with mass and inertia

Parameters:

  • m: [kg] mass of the body
  • I: [kg.m²] inertia of the body with respect to the origin of frame along the z-axis of frame
  • r: [m, m] Translational position x,y-position
  • gy: [m/s²] gravity field acting on the mass in the y-direction, positive value acts in the positive direction defaults to -9.80665
  • radius: [m] Radius of the body in animations
  • render: [Bool] Render the body in animations
  • color: [Array{Float64,1}] Color of the body in animations

Variables:

  • r: [m, m] x,y position
  • v: [m/s, m/s] x,y velocity
  • a: [m/s², m/s²] x,y acceleration
  • phi: [rad] rotation angle (counterclockwise)
  • w: [rad/s] angular velocity
  • α: [rad/s²] angular acceleration

Connectors:

  • frame: 2-dim. Coordinate system
source
Multibody.SlippingWheelMethod
SlippingWheel(; name, radius, m, I_axis, I_long, width = 0.035, x0=0, z0=0,
+                  angles = zeros(3), der_angles = zeros(3), kwargs...)

Wheel with slip rolling on a surface.

Parameters

  • radius: Radius of the wheel
  • m: Mass of the wheel
  • I_axis: Moment of inertia of the wheel along its axis
  • I_long: Moment of inertia of the wheel perpendicular to its axis
  • width: Width of the wheel (for rendering)
  • x0: Initial x-position of the wheel axis
  • z0: Initial z-position of the wheel axis
  • state: (structural) whether or not the component has angular state variables.

Variables

  • x: x-position of the wheel axis
  • z: z-position of the wheel axis
  • angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
  • der_angles: Derivatives of angles

Connectors

  • frame_a: Frame for the wheel component

Examples

See Docs: Wheels

source
Multibody.PlanarMechanics.BodyShapeConstant
BodyShape(; name, r = [1,0], r_cm = 0.5*r, gy = -9.80665)

The BodyShape component is similar to a Body, but it has two frames and a vector r that describes the translation between them, while the body has a single frame only.

Parameters

  • r: (Structural) Vector from frame_a to frame_b resolved in frame_a
  • r_cm: (Structural) Vector from frame_a to the center of mass resolved in frame_a

Subsystems

  • translation: FixedTranslation Fixed translation between frame_a and frame_b
  • translation_cm: FixedTranslation Fixed translation between frame_a and the center of mass
  • body: Body Body component placed at center of mass. This component holds the inertial properties

Connectors

  • frame_a
  • frame_b
source
Multibody.PlanarMechanics.DamperConstant
Damper(; name, d = 1, s_small = 1.e-10)

Linear (velocity dependent) damper

Parameters:

  • d: [N.s/m] Damping constant
  • s_small: [m] Prevent zero-division if distance between framea and frameb is zero

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.DifferentialGearConstant
DifferentialGear(; name)

A 1D-rotational component that is a variant of a planetary gear and can be used to distribute the torque equally among the wheels on one axis.

Connectors:

  • flange_b (Rotational.Flange) Flange for the input torque
  • flange_left (Rotational.Flange) Flange for the left output torque
  • flange_right (Rotational.Flange) Flange for the right output torque
source
Multibody.PlanarMechanics.FixedConstant
Fixed(; name, r = [0.0, 0.0], phi = 0.0)

Frame fixed in the planar world frame at a given position and orientation

Parameters:

  • r: [m, m] Fixed absolute x,y-position, resolved in world frame
  • phi: [rad] Fixed angle

Connectors:

  • frame_b: 2-dim. Coordinate system
source
Multibody.PlanarMechanics.FixedTranslationConstant
FixedTranslation(; name, r::AbstractArray, l)

A fixed translation between two components (rigid rod)

Parameters:

  • rx: [m] Fixed x-length of the rod resolved w.r.t to body frame_a at phi = 0
  • ry: [m] Fixed y-length of the rod resolved w.r.t to body frame_a at phi = 0
  • radius: [m] Radius of the rod in animations
  • render: [Bool] Render the rod in animations

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.IdealPlanetaryConstant
IdealPlanetary(; name, ratio = 2)

The IdealPlanetary gear box is an ideal gear without inertia, elasticity, damping or backlash consisting of an inner sun wheel, an outer ring wheel and a planet wheel located between sun and ring wheel. The bearing of the planet wheel shaft is fixed in the planet carrier. The component can be connected to other elements at the sun, ring and/or carrier flanges. It is not possible to connect to the planet wheel. If inertia shall not be neglected, the sun, ring and carrier inertias can be easily added by attaching inertias (= model Inertia) to the corresponding connectors. The inertias of the planet wheels are always neglected.

The ideal planetary gearbox is uniquely defined by the ratio of the number of ring teeth $z_r$ with respect to the number of sun teeth $z_s$. For example, if there are 100 ring teeth and 50 sun teeth then ratio = $z_r/z_s = 2$. The number of planet teeth $z_p$ has to fulfill the following relationship:

\[z_p = (z_r - z_s) / 2\]

Therefore, in the above example $z_p = 25$ is required.

According to the overall convention, the positive direction of all vectors, especially the absolute angular velocities and cut-torques in the flanges, are along the axis vector displayed in the icon.

Parameters:

  • ratio: Number of ring teeth/sun teeth

Connectors:

  • sun (Rotational.Flange) Sun wheel
  • carrier (Rotational.Flange) Planet carrier
  • ring (Rotational.Flange) Ring wheel
source
Multibody.PlanarMechanics.SimpleWheelConstant
SimpleWheel(; name, radius = 0.3, color = [1, 0, 0, 1], μ = 1e9)

Simple wheel model with viscous lateral friction and a driving torque

Connectors:

  • frame_a (Frame) Coordinate system fixed to the component with one cut-force and cut-torque
  • thrust (RealInput) Input for the longitudinal force applied to the wheel

Parameters:

  • μ: [Ns/m] Viscous friction coefficient
  • radius: [m] Radius of the wheel
  • color: Color of the wheel in animations

Variables:

  • θ: [rad] Wheel angle
  • Vx: [m/s] Longitudinal velocity (resolved in local frame)
  • Vy: [m/s] Lateral velocity (resolved in local frame)
  • Fy: [N] Lateral friction force
  • Fx: [N] Applied longitudinal wheel force
source
Multibody.PlanarMechanics.SpringConstant
Spring(; name, c_x = 1, c_y = 1, c_phi = 1e5, s_relx0 = 0, s_rely0 = 0, phi_rel0 = 0, s_small = 1.e-10)

Linear 2D translational spring

Parameters:

  • c_x: [N/m] Spring constant in x dir
  • c_y: [N/m] Spring constant in y dir
  • c_phi: [N.m/rad] Spring constant in phi dir
  • s_relx0: [m] Unstretched spring length
  • s_rely0: [m] Unstretched spring length
  • phi_rel0: [rad] Unstretched spring angle
  • s_small: [m] Prevent zero-division if distance between framea and frameb is zero
  • num_windings: [Int] Number of windings of the coil when rendered
  • color = [0,0,1,1] Color of the spring in animations
  • render = true Render the spring in animations
  • radius = 0.1 Radius of spring when rendered
  • N = 200 Number of points in mesh when rendered

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.SpringDamperConstant
SpringDamper(; name, c_x = 1, c_y = 1, c_phi = 1e5, d_x = 1, d_y = 1, d_phi = 1, s_relx0 = 0, s_rely0 = 0, phi_rel0 = 0, s_small = 1.e-10)

Linear 2D translational spring damper model

Parameters:

  • c_x: [N/m] Spring constant in x dir
  • c_y: [N/m] Spring constant in y dir
  • c_phi: [N.m/rad] Spring constant in phi dir
  • d_x: [N.s/m] Damping constant in x dir
  • d_y: [N.s/m] Damping constant in y dir
  • d_phi: [N.m.s/rad] Damping constant in phi dir
  • s_relx0: [m] Unstretched spring length
  • s_rely0: [m] Unstretched spring length
  • phi_rel0: [rad] Unstretched spring angle
  • s_small: [m] Prevent zero-division if distance between framea and frameb is zero
  • num_windings: [Int] Number of windings of the coil when rendered
  • color = [0,0,1,1] Color of the spring in animations
  • render = true Render the spring in animations
  • radius = 0.1 Radius of spring when rendered
  • N = 200 Number of points in mesh when rendered

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.BodyMethod
Body(; name, m=1, I=0.1, r=0, gy=-9.80665, radius=0.1, render=true, color=Multibody.purple)

Body component with mass and inertia

Parameters:

  • m: [kg] mass of the body
  • I: [kg.m²] inertia of the body with respect to the origin of frame along the z-axis of frame
  • r: [m, m] Translational position x,y-position
  • gy: [m/s²] gravity field acting on the mass in the y-direction, positive value acts in the positive direction defaults to -9.80665
  • radius: [m] Radius of the body in animations
  • render: [Bool] Render the body in animations
  • color: [Array{Float64,1}] Color of the body in animations

Variables:

  • r: [m, m] x,y position
  • v: [m/s, m/s] x,y velocity
  • a: [m/s², m/s²] x,y acceleration
  • phi: [rad] rotation angle (counterclockwise)
  • w: [rad/s] angular velocity
  • α: [rad/s²] angular acceleration

Connectors:

  • frame: 2-dim. Coordinate system
source
Multibody.PlanarMechanics.SlipBasedWheelJointMethod
SlipBasedWheelJoint(;
     name,
     r = [1, 0],
     N,
@@ -74,7 +74,7 @@
     width = diameter * 0.6,
     radius = 0.1,
     w_roll = nothing,
-)

Slip-based wheel joint

The ideal wheel joint models the behavior of a wheel rolling on a x,y-plane whose contact patch has slip-dependent friction characteristics. This is an approximation for wheels with a rim and a rubber tire.

The force depends with friction characteristics on the slip. The slip is split into two components:

  • lateral slip: the lateral velocity divided by the rolling velocity.
  • longitudinal slip: the longitudinal slip velocity divided by the rolling velocity.

For low rolling velocity this definition become ill-conditioned. Hence a dry-friction model is used for low rolling velocities. For zero rolling velocity, the intitialization might fail if automatic differentiation is used. Either start with a non-zero (but tiny) rolling velocity or pass autodiff=false to the solver.

The radius of the wheel can be specified by the parameter radius. The driving direction (for phi = 0) can be specified by the parameter r. The normal load is set by N.

The wheel contains a 2D connector frame_a for the steering on the plane. The rolling motion of the wheel can be actuated by the 1D connector flange_a.

In addition there is an input dynamicLoad for a dynamic component of the normal load.

Connectors:

  • frame_a (Frame) Coordinate system fixed to the component with one cut-force and cut-torque
  • flange_a (Rotational.Flange) Flange for the rolling motion
  • dynamicLoad (Blocks.RealInput) Input for the dynamic component of the normal load (must be connected)

Terminology:

  • Adhesion refers to the peak of the traction curve, where the slip is such that the maximum amount of traction is generated.
  • Sliding velocity refers to the velocity at which the traction curve saturates and stays constant with increased slip velocity.
source
Multibody.PlanarMechanics.limit_S_formMethod
limit_S_form(x_min, x_max, y_min, y_max, x)

Returns a S-shaped transition

A smooth transition between points (x_min, y_min) and (x_max, y_max). The transition is done in such a way that the function's 1st derivative is continuous for all x. The higher derivatives are discontinuous at input points.

x_min = -0.4
+)

Slip-based wheel joint

The ideal wheel joint models the behavior of a wheel rolling on a x,y-plane whose contact patch has slip-dependent friction characteristics. This is an approximation for wheels with a rim and a rubber tire.

The force depends with friction characteristics on the slip. The slip is split into two components:

  • lateral slip: the lateral velocity divided by the rolling velocity.
  • longitudinal slip: the longitudinal slip velocity divided by the rolling velocity.

For low rolling velocity this definition become ill-conditioned. Hence a dry-friction model is used for low rolling velocities. For zero rolling velocity, the intitialization might fail if automatic differentiation is used. Either start with a non-zero (but tiny) rolling velocity or pass autodiff=false to the solver.

The radius of the wheel can be specified by the parameter radius. The driving direction (for phi = 0) can be specified by the parameter r. The normal load is set by N.

The wheel contains a 2D connector frame_a for the steering on the plane. The rolling motion of the wheel can be actuated by the 1D connector flange_a.

In addition there is an input dynamicLoad for a dynamic component of the normal load.

Connectors:

  • frame_a (Frame) Coordinate system fixed to the component with one cut-force and cut-torque
  • flange_a (Rotational.Flange) Flange for the rolling motion
  • dynamicLoad (Blocks.RealInput) Input for the dynamic component of the normal load (must be connected)

Terminology:

  • Adhesion refers to the peak of the traction curve, where the slip is such that the maximum amount of traction is generated.
  • Sliding velocity refers to the velocity at which the traction curve saturates and stays constant with increased slip velocity.
source
Multibody.PlanarMechanics.limit_S_formMethod
limit_S_form(x_min, x_max, y_min, y_max, x)

Returns a S-shaped transition

A smooth transition between points (x_min, y_min) and (x_max, y_max). The transition is done in such a way that the function's 1st derivative is continuous for all x. The higher derivatives are discontinuous at input points.

x_min = -0.4
 x_max = 0.6
 y_max = 1.4
 y_min = 1.2
@@ -97,7 +97,7 @@
          │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡔⠁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
    1.194 │⠀⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠖⠉⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
          └────────────────────────────────────────┘ 
-         ⠀-1.06⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀1.06⠀ 
source
Multibody.PlanarMechanics.limit_S_tripleMethod
limit_S_triple(x_max, x_sat, y_max, y_sat, x)

Returns a point-symmetric Triple S-Function

A point symmetric interpolation between points (0, 0), (x_max, y_max) and (x_sat, y_sat), provided x_max < x_sat. The approximation is done in such a way that the function's 1st derivative is zero at points (x_max, y_max) and (x_sat, y_sat). Thus, the function's 1st derivative is continuous for all x. The higher derivatives are discontinuous at these points.

x_max = 0.2
+         ⠀-1.06⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀1.06⠀ 
source
Multibody.PlanarMechanics.limit_S_tripleMethod
limit_S_triple(x_max, x_sat, y_max, y_sat, x)

Returns a point-symmetric Triple S-Function

A point symmetric interpolation between points (0, 0), (x_max, y_max) and (x_sat, y_sat), provided x_max < x_sat. The approximation is done in such a way that the function's 1st derivative is zero at points (x_max, y_max) and (x_sat, y_sat). Thus, the function's 1st derivative is continuous for all x. The higher derivatives are discontinuous at these points.

x_max = 0.2
 x_sat = 0.5
 y_max = 1.4
 y_sat = 1.2
@@ -122,4 +122,4 @@
             │⠀⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⣀⡀⠀⠀⠀⡜⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
    -1.48377 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠦⠼⠁⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
             └────────────────────────────────────────┘ 
-            ⠀-1.06⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀1.06⠀ 
source
+ ⠀-1.06⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀1.06⠀ source diff --git a/dev/examples/free_motion/99ce2bfc.svg b/dev/examples/free_motion/672a23aa.svg similarity index 89% rename from dev/examples/free_motion/99ce2bfc.svg rename to dev/examples/free_motion/672a23aa.svg index f7e2a95b..3967200b 100644 --- a/dev/examples/free_motion/99ce2bfc.svg +++ b/dev/examples/free_motion/672a23aa.svg @@ -1,52 +1,52 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/free_motion/8358228b.svg b/dev/examples/free_motion/99cd2fcf.svg similarity index 93% rename from dev/examples/free_motion/8358228b.svg rename to dev/examples/free_motion/99cd2fcf.svg index c0b6ab7e..4630ca36 100644 --- a/dev/examples/free_motion/8358228b.svg +++ b/dev/examples/free_motion/99cd2fcf.svg @@ -1,48 +1,48 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/free_motion/index.html b/dev/examples/free_motion/index.html index 76e66780..a87a161a 100644 --- a/dev/examples/free_motion/index.html +++ b/dev/examples/free_motion/index.html @@ -28,7 +28,7 @@ # Plot analytical solution tvec = 0:0.1:sol.t[end] -plot!(tvec, -9.81/2 .* tvec .^ 2, lab="Analytical solution")Example block output

The figure indicates that the body is falling freely, experiencing a constant acceleration of -9.81 m/s² in the $y$ direction, corresponding to the gravity parameters of the world:

Model world with 19 (25) equations
+plot!(tvec, -9.81/2 .* tvec .^ 2, lab="Analytical solution")
Example block output

The figure indicates that the body is falling freely, experiencing a constant acceleration of -9.81 m/s² in the $y$ direction, corresponding to the gravity parameters of the world:

Model world with 19 (25) equations
 Unknowns (16):
   (n_inner(t))[1]
   (n_inner(t))[2]
@@ -92,5 +92,5 @@
 @assert SciMLBase.successful_retcode(sol)
 
 plot(sol, idxs = [body.r_0...])
-# plot(sol, idxs = [body.body.Q...])
Example block output
import GLMakie
-Multibody.render(model, sol, filename = "free_body.gif")

free_body

+# plot(sol, idxs = [body.body.Q...])Example block output
import GLMakie
+Multibody.render(model, sol, filename = "free_body.gif")

free_body

diff --git a/dev/examples/gearbox/549154bf.svg b/dev/examples/gearbox/bbc711da.svg similarity index 90% rename from dev/examples/gearbox/549154bf.svg rename to dev/examples/gearbox/bbc711da.svg index 90504474..8ce17bd1 100644 --- a/dev/examples/gearbox/549154bf.svg +++ b/dev/examples/gearbox/bbc711da.svg @@ -1,120 +1,120 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/gearbox/index.html b/dev/examples/gearbox/index.html index e2c48afb..a9ceae7a 100644 --- a/dev/examples/gearbox/index.html +++ b/dev/examples/gearbox/index.html @@ -52,4 +52,4 @@ gearConstraint.actuatedRevolute_a.phi gearConstraint.phi_b inertia1.phi / inertia2.phi # One can plot arbitrary expressions of variables! In this case, we plot the ratio between the two angles. -], layout=3, sp=[1 2 1 2 3], framestyle=:zerolines)Example block output

The plot indicates that the ratio between the angles of inertia 1 and 2 is 10, as expected, and that the same ratio holds between the two sides of the 3D gear constraint.

+], layout=3, sp=[1 2 1 2 3], framestyle=:zerolines)Example block output

The plot indicates that the ratio between the angles of inertia 1 and 2 is 10, as expected, and that the same ratio holds between the two sides of the 3D gear constraint.

diff --git a/dev/examples/gyroscopic_effects/index.html b/dev/examples/gyroscopic_effects/index.html index 220a7825..98b4d58e 100644 --- a/dev/examples/gyroscopic_effects/index.html +++ b/dev/examples/gyroscopic_effects/index.html @@ -37,4 +37,4 @@ @assert SciMLBase.successful_retcode(sol) import GLMakie -Multibody.render(model, sol; x=1, z=1, filename = "gyro.gif") # Use "gyro.mp4" for a video file

animation

Try setting model.revolute.w => 0 and plot this variable using plot(sol, idxs=model.revolute.w) and you will notice that the swinging of the pendulum induces a rotation around this joint, even if it has no rotational velocity from the start.

+Multibody.render(model, sol; x=1, z=1, filename = "gyro.gif") # Use "gyro.mp4" for a video file

animation

Try setting model.revolute.w => 0 and plot this variable using plot(sol, idxs=model.revolute.w) and you will notice that the swinging of the pendulum induces a rotation around this joint, even if it has no rotational velocity from the start.

diff --git a/dev/examples/kinematic_loops/05bfe82a.svg b/dev/examples/kinematic_loops/1789cc45.svg similarity index 89% rename from dev/examples/kinematic_loops/05bfe82a.svg rename to dev/examples/kinematic_loops/1789cc45.svg index c70211ae..05487a0d 100644 --- a/dev/examples/kinematic_loops/05bfe82a.svg +++ b/dev/examples/kinematic_loops/1789cc45.svg @@ -1,48 +1,48 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/kinematic_loops/2399a07e.svg b/dev/examples/kinematic_loops/8d311527.svg similarity index 98% rename from dev/examples/kinematic_loops/2399a07e.svg rename to dev/examples/kinematic_loops/8d311527.svg index bec62ab2..b5938ade 100644 --- a/dev/examples/kinematic_loops/2399a07e.svg +++ b/dev/examples/kinematic_loops/8d311527.svg @@ -1,96 +1,96 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/kinematic_loops/e1c9b07c.svg b/dev/examples/kinematic_loops/9e8f3844.svg similarity index 91% rename from dev/examples/kinematic_loops/e1c9b07c.svg rename to dev/examples/kinematic_loops/9e8f3844.svg index f9341ed6..28a50117 100644 --- a/dev/examples/kinematic_loops/e1c9b07c.svg +++ b/dev/examples/kinematic_loops/9e8f3844.svg @@ -1,50 +1,50 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/kinematic_loops/index.html b/dev/examples/kinematic_loops/index.html index 2e78e6c8..9490cd56 100644 --- a/dev/examples/kinematic_loops/index.html +++ b/dev/examples/kinematic_loops/index.html @@ -58,7 +58,7 @@ plot( plot(sol, idxs = [j1.phi, j2.phi, j3.phi]), plot(sol, idxs = [j1.w, j2.w, j3.w]), -)Example block output
using Test
+)
Example block output
using Test
 @test SciMLBase.successful_retcode(sol)
Test Passed

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
 Multibody.render(fourbar, sol, 0:0.05:10; x=4, y=-1, z=4, lookat=[0, -1, 0], filename = "fourbar.gif") # Use "fourbar.mp4" for a video file

animation

Using cut joints

The mechanism below is another instance of a 4-bar linkage, this time with 6 revolute joints, 1 prismatic joint and 4 bodies.

We show two different ways of modeling this mechanism, first using a cut joint, and later using a much more efficient joint assembly.

In order to simulate this mechanism using a cut joint, the user must

  1. Use the iscut=true keyword argument to one of the Revolute joints to indicate that the joint is a cut joint. A cut joint behaves similarly to a regular joint, but it introduces fewer constraints in order to avoid the otherwise over-constrained system resulting from closing the kinematic loop. While almost any joint can be chosen as the cut joint, it might be worthwhile experimenting with this choice in order to get an efficient representation. In this example, cutting j5 produces an 8-dimensional state realization, while all other joints result in a 17-dimensional state.
  2. Increase the state_priority of the joint j1 above the default joint priority 3. This encourages the model compiler to choose the joint coordinate of j1 as state variable. The joint coordinate of j1 is the only coordinate that uniquely determines the configuration of the mechanism. The choice of any other joint coordinate would lead to a singular representation in at least one configuration of the mechanism. The joint j1 is the revolute joint located in the origin, see the animation below where this joint is made larger than the others.

To drive the mechanism, we set the initial velocity of the joint j1 to some non-zero value.

systems = @named begin
     j1 = Revolute(n = [1, 0, 0], w0 = 5.235987755982989, state_priority=10.0, radius=0.1f0) # Increase state priority to ensure that this joint coordinate is chosen as state variable
@@ -95,7 +95,7 @@
 
 sol = solve(prob, FBDF(autodiff=true));
 @test SciMLBase.successful_retcode(sol)
-plot(sol, idxs=[j2.s]) # Plot the joint coordinate of the prismatic joint (green in the animation below)
Example block output
import GLMakie
+plot(sol, idxs=[j2.s]) # Plot the joint coordinate of the prismatic joint (green in the animation below)
Example block output
import GLMakie
 Multibody.render(fourbar2, sol; x=-2, y = 2, z = 3, filename = "fourbar2.gif") # Use "fourbar2.mp4" for a video file

animation

Using a joint assembly

This example models a mechanism similar to the previous one, but replaces several joints and bodies with the aggregate UniversalSpherical joint. This joint is a combination of a universal joint and a spherical joint, with a bar in-between. A benefit of using this joint assembly in a kinematic loop is that some nonlinear equations are solved analytically, and the solver will thus see fewer nonlinear equations. This can lead to a faster simulation.

systems = @named begin
     j1 = Revolute(n = [1, 0, 0], w0 = 5.235987755983, state_priority=12.0, radius=0.1f0) # Increase state priority to ensure that this joint coordinate is chosen as state variable
     j2 = Prismatic(n = [1, 0, 0], s0 = -0.2)
@@ -119,6 +119,6 @@
 ssys_analytic = structural_simplify(multibody(fourbar_analytic))
 prob = ODEProblem(ssys_analytic, [], (0.0, 1.4399))
 sol2 = solve(prob, FBDF(autodiff=true)) # about 4x faster than the simulation above
-plot!(sol2, idxs=[j2.s]) # Plot the same coordinate as above
Example block output

In practice, the two simulations are not exactly identical since we haven't modeled any mass attached to the rod in the joint assembly. We could add such mass to the rod by attaching to the joint_us.frame_ia connector.

import GLMakie
+plot!(sol2, idxs=[j2.s]) # Plot the same coordinate as above
Example block output

In practice, the two simulations are not exactly identical since we haven't modeled any mass attached to the rod in the joint assembly. We could add such mass to the rod by attaching to the joint_us.frame_ia connector.

import GLMakie
 Multibody.render(fourbar_analytic, sol2; x=-2, y = 2, z = 3, filename = "fourbar_analytic.gif")

animation

While the version with a cut joint were solving for

length(unknowns(ssys))
8

variables, the version with the joint assembly solved for only

length(unknowns(ssys_analytic))
3

variables.

We can also inspect the mass matrices of the two systems to see how many nonlinear algebraic equations the solver has to deal with

using LinearAlgebra
-diag(ssys.mass_matrix), diag(ssys_analytic.mass_matrix)
([1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1])

A 1 on the diagonal indicates a differential equation, while a 0 indicates an algebraic equation. The cut-joint version has 6 nonlinear algebraic equations, while the joint assembly version has only 1. Both of them have 2 differential equations (position and velocity), corresponding to the 1 degree of freedom in the mechanism. Nonlinear algebraic equations are more expensive to solve than differential equations.

+diag(ssys.mass_matrix), diag(ssys_analytic.mass_matrix)
([1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1])

A 1 on the diagonal indicates a differential equation, while a 0 indicates an algebraic equation. The cut-joint version has 6 nonlinear algebraic equations, while the joint assembly version has only 1. Both of them have 2 differential equations (position and velocity), corresponding to the 1 degree of freedom in the mechanism. Nonlinear algebraic equations are more expensive to solve than differential equations.

diff --git a/dev/examples/pendulum/bc93fb68.svg b/dev/examples/pendulum/1019d232.svg similarity index 89% rename from dev/examples/pendulum/bc93fb68.svg rename to dev/examples/pendulum/1019d232.svg index 4b5a49e0..46cdc348 100644 --- a/dev/examples/pendulum/bc93fb68.svg +++ b/dev/examples/pendulum/1019d232.svg @@ -1,46 +1,46 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/12fb8a2f.svg b/dev/examples/pendulum/12fb8a2f.svg new file mode 100644 index 00000000..93287cac --- /dev/null +++ b/dev/examples/pendulum/12fb8a2f.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/c70497ec.svg b/dev/examples/pendulum/210d83bb.svg similarity index 89% rename from dev/examples/pendulum/c70497ec.svg rename to dev/examples/pendulum/210d83bb.svg index bf10ac6c..ef4c9e63 100644 --- a/dev/examples/pendulum/c70497ec.svg +++ b/dev/examples/pendulum/210d83bb.svg @@ -1,154 +1,154 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/bb1ef008.svg b/dev/examples/pendulum/2fd21779.svg similarity index 90% rename from dev/examples/pendulum/bb1ef008.svg rename to dev/examples/pendulum/2fd21779.svg index 572be29a..caa9b5b5 100644 --- a/dev/examples/pendulum/bb1ef008.svg +++ b/dev/examples/pendulum/2fd21779.svg @@ -1,116 +1,116 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/b85f93fa.svg b/dev/examples/pendulum/30b752fb.svg similarity index 88% rename from dev/examples/pendulum/b85f93fa.svg rename to dev/examples/pendulum/30b752fb.svg index 5c510561..396d5723 100644 --- a/dev/examples/pendulum/b85f93fa.svg +++ b/dev/examples/pendulum/30b752fb.svg @@ -1,50 +1,50 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/b5bdcd1b.svg b/dev/examples/pendulum/4525f341.svg similarity index 91% rename from dev/examples/pendulum/b5bdcd1b.svg rename to dev/examples/pendulum/4525f341.svg index 12327cd2..d0cf5ffd 100644 --- a/dev/examples/pendulum/b5bdcd1b.svg +++ b/dev/examples/pendulum/4525f341.svg @@ -1,152 +1,152 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/69d35a6e.svg b/dev/examples/pendulum/7ff8455f.svg similarity index 90% rename from dev/examples/pendulum/69d35a6e.svg rename to dev/examples/pendulum/7ff8455f.svg index d0f83b40..74013613 100644 --- a/dev/examples/pendulum/69d35a6e.svg +++ b/dev/examples/pendulum/7ff8455f.svg @@ -1,42 +1,42 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/8d4190d0.svg b/dev/examples/pendulum/8f074caf.svg similarity index 88% rename from dev/examples/pendulum/8d4190d0.svg rename to dev/examples/pendulum/8f074caf.svg index d3740e6b..3e2b1d09 100644 --- a/dev/examples/pendulum/8d4190d0.svg +++ b/dev/examples/pendulum/8f074caf.svg @@ -1,48 +1,48 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/8f719bec.svg b/dev/examples/pendulum/8f719bec.svg deleted file mode 100644 index 59754745..00000000 --- a/dev/examples/pendulum/8f719bec.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dev/examples/pendulum/37a591d3.svg b/dev/examples/pendulum/bf755513.svg similarity index 96% rename from dev/examples/pendulum/37a591d3.svg rename to dev/examples/pendulum/bf755513.svg index 98bac8ec..6e6b828a 100644 --- a/dev/examples/pendulum/37a591d3.svg +++ b/dev/examples/pendulum/bf755513.svg @@ -1,158 +1,158 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/pendulum/index.html b/dev/examples/pendulum/index.html index 9867edda..eefaf5e7 100644 --- a/dev/examples/pendulum/index.html +++ b/dev/examples/pendulum/index.html @@ -38,12 +38,12 @@ ]

With all components and connections defined, we can create an ODESystem like so:

@named model = ODESystem(connections, t, systems=[world, joint, body])
 model = complete(model)

The ODESystem is the fundamental model type in ModelingToolkit used for multibody-type models.

Before we can simulate the system, we must perform model check using the function multibody and compilation using structural_simplify

ssys = structural_simplify(multibody(model))
ScheduledSystem with state (2):
  joint₊phi
- joint₊w

This results in a simplified model with the minimum required variables and equations to be able to simulate the system efficiently. This step rewrites all connect statements into the appropriate equations, and removes any redundant variables and equations. To simulate the pendulum, we require two state variables, one for angle and one for angular velocity, we can see above that these state variables have indeed been chosen.

Multibody.multibodyFunction
multibody(model)

Perform validity checks on the model, such as the precense of exactly one world component in the top level of the model, and transform the model into an IRSystem object for passing into structural_simplify.

source

We are now ready to create an ODEProblem and simulate it. We use the Rodas4 solver from OrdinaryDiffEq.jl, and pass a dictionary for the initial conditions. We specify only initial condition for some variables, for those variables where no initial condition is specified, the default initial condition defined the model will be used.

D = Differential(t)
+ joint₊w

This results in a simplified model with the minimum required variables and equations to be able to simulate the system efficiently. This step rewrites all connect statements into the appropriate equations, and removes any redundant variables and equations. To simulate the pendulum, we require two state variables, one for angle and one for angular velocity, we can see above that these state variables have indeed been chosen.

Multibody.multibodyFunction
multibody(model)

Perform validity checks on the model, such as the precense of exactly one world component in the top level of the model, and transform the model into an IRSystem object for passing into structural_simplify.

source

We are now ready to create an ODEProblem and simulate it. We use the Rodas4 solver from OrdinaryDiffEq.jl, and pass a dictionary for the initial conditions. We specify only initial condition for some variables, for those variables where no initial condition is specified, the default initial condition defined the model will be used.

D = Differential(t)
 defs = Dict() # We may specify the initial condition here
 prob = ODEProblem(ssys, defs, (0, 3.35))
 
 sol = solve(prob, Rodas4())
-plot(sol, idxs = joint.phi, title="Pendulum")
Example block output

The solution sol can be plotted directly if the Plots package is loaded. The figure indicates that the pendulum swings back and forth without any damping. To add damping as well, we could add a Damper from the ModelingToolkitStandardLibrary.Mechanical.Rotational module to the revolute joint. We do this below

3D Animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie # GLMakie is another alternative, suitable for interactive plots
+plot(sol, idxs = joint.phi, title="Pendulum")
Example block output

The solution sol can be plotted directly if the Plots package is loaded. The figure indicates that the pendulum swings back and forth without any damping. To add damping as well, we could add a Damper from the ModelingToolkitStandardLibrary.Mechanical.Rotational module to the revolute joint. We do this below

3D Animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie # GLMakie is another alternative, suitable for interactive plots
 Multibody.render(model, sol; filename = "pendulum.gif") # Use "pendulum.mp4" for a video file

animation

By default, the world frame is indicated using the convention x: red, y: green, z: blue. The animation shows how the simple Body represents a point mass with inertial properties at a particular distance r_cm away from its mounting flange frame_a. The cylinder that is shown connecting the pivot point to the body is for visualization purposes only, it does not have any inertial properties. To model a more physically motivated pendulum rod, we could have used a BodyShape component, which has two mounting flanges instead of one. The BodyShape component is shown in several of the examples available in the example sections of the documentation.

Adding damping

To add damping to the pendulum such that the pendulum will eventually come to rest, we add a Damper to the revolute joint. The damping coefficient is given by d, and the damping force is proportional to the angular velocity of the joint. To add the damper to the revolute joint, we must create the joint with the keyword argument axisflange = true, this adds two internal flanges to the joint to which you can attach components from the ModelingToolkitStandardLibrary.Mechanical.Rotational module (1-dimensional components). We then connect one of the flanges of the damper to the axis flange of the joint, and the other damper flange to the support flange which is rigidly attached to the world.

@named damper = Rotational.Damper(d = 0.3)
 @named joint = Revolute(n = [0, 0, 1], isroot = true, axisflange = true)
 
@@ -59,7 +59,7 @@
 prob = ODEProblem(ssys, [damper.phi_rel => 1], (0, 10))
 
 sol = solve(prob, Rodas4())
-plot(sol, idxs = joint.phi, title="Damped pendulum")
Example block output

This time we see that the pendulum loses energy and eventually comes to rest at the stable equilibrium point $\pi / 2$.

Multibody.render(model, sol; filename = "pendulum_damped.gif")

animation damped

A linear pendulum?

When we think of a pendulum, we typically think of a rotary pendulum that is rotating around a pivot point like in the examples above. A mass suspended in a spring can be though of as a linear pendulum (often referred to as a harmonic oscillator rather than a pendulum), and we show here how we can construct a model of such a device. This time around, we make use of a Prismatic joint rather than a Revolute joint. A prismatic joint has one positional degree of freedom, compared to the single rotational degree of freedom for the revolute joint.

@named body_0 = Body(; m = 1, isroot = false, r_cm = [0, 0, 0])
+plot(sol, idxs = joint.phi, title="Damped pendulum")
Example block output

This time we see that the pendulum loses energy and eventually comes to rest at the stable equilibrium point $\pi / 2$.

Multibody.render(model, sol; filename = "pendulum_damped.gif")

animation damped

A linear pendulum?

When we think of a pendulum, we typically think of a rotary pendulum that is rotating around a pivot point like in the examples above. A mass suspended in a spring can be though of as a linear pendulum (often referred to as a harmonic oscillator rather than a pendulum), and we show here how we can construct a model of such a device. This time around, we make use of a Prismatic joint rather than a Revolute joint. A prismatic joint has one positional degree of freedom, compared to the single rotational degree of freedom for the revolute joint.

@named body_0 = Body(; m = 1, isroot = false, r_cm = [0, 0, 0])
 @named damper = Translational.Damper(d=1)
 @named spring = Translational.Spring(c=10)
 @named joint = Prismatic(n = [0, 1, 0], axisflange = true)
@@ -76,7 +76,7 @@
 prob = ODEProblem(ssys, [], (0, 10))
 
 sol = solve(prob, Rodas4())
-Plots.plot(sol, idxs = joint.s, title="Mass-spring-damper system")
Example block output

As is hopefully evident from the little code snippet above, this linear pendulum model has a lot in common with the rotary pendulum. In this example, we connected both the spring and a damper to the same axis flange in the joint. This time, the components came from the Translational submodule of ModelingToolkitStandardLibrary rather than the Rotational submodule. Also here do we pass axisflange when we create the joint to make sure that it is equipped with the flanges support and axis needed to connect the translational components.

Multibody.render(model, sol; filename = "linear_pend.gif", framerate=24)

linear pendulum

Why do we need a joint?

In the example above, we introduced a prismatic joint to model the oscillating motion of the mass-spring system. In reality, we can suspend a mass in a spring without any joint, so why do we need one here? The answer is that we do not, in fact, need the joint, but if we connect the spring directly to the world, we need to make the body (mass) the root object of the kinematic tree instead:

@named root_body = Body(; m = 1, isroot = true, r_cm = [0, 1, 0], phi0 = [0, 1, 0])
+Plots.plot(sol, idxs = joint.s, title="Mass-spring-damper system")
Example block output

As is hopefully evident from the little code snippet above, this linear pendulum model has a lot in common with the rotary pendulum. In this example, we connected both the spring and a damper to the same axis flange in the joint. This time, the components came from the Translational submodule of ModelingToolkitStandardLibrary rather than the Rotational submodule. Also here do we pass axisflange when we create the joint to make sure that it is equipped with the flanges support and axis needed to connect the translational components.

Multibody.render(model, sol; filename = "linear_pend.gif", framerate=24)

linear pendulum

Why do we need a joint?

In the example above, we introduced a prismatic joint to model the oscillating motion of the mass-spring system. In reality, we can suspend a mass in a spring without any joint, so why do we need one here? The answer is that we do not, in fact, need the joint, but if we connect the spring directly to the world, we need to make the body (mass) the root object of the kinematic tree instead:

@named root_body = Body(; m = 1, isroot = true, r_cm = [0, 1, 0], phi0 = [0, 1, 0])
 @named multibody_spring = Multibody.Spring(c=10)
 
 connections = [connect(world.frame_b, multibody_spring.frame_a)
@@ -91,7 +91,7 @@
 prob = ODEProblem(ssys, defs, (0, 10))
 
 sol = solve(prob, Rodas4())
-plot(sol, idxs = multibody_spring.r_rel_0[2], title="Mass-spring system without joint")
Example block output

Here, we used a Multibody.Spring instead of connecting a Translational.Spring to a joint. The Translational.Spring, alongside other components from ModelingToolkitStandardLibrary.Mechanical, is a 1-dimensional object, whereas multibody components are 3-dimensional objects.

Internally, the Multibody.Spring contains a Translational.Spring, attached between two flanges, so we could actually add a damper to the system as well:

push!(connections, connect(multibody_spring.spring2d.flange_a, damper.flange_a))
+plot(sol, idxs = multibody_spring.r_rel_0[2], title="Mass-spring system without joint")
Example block output

Here, we used a Multibody.Spring instead of connecting a Translational.Spring to a joint. The Translational.Spring, alongside other components from ModelingToolkitStandardLibrary.Mechanical, is a 1-dimensional object, whereas multibody components are 3-dimensional objects.

Internally, the Multibody.Spring contains a Translational.Spring, attached between two flanges, so we could actually add a damper to the system as well:

push!(connections, connect(multibody_spring.spring2d.flange_a, damper.flange_a))
 push!(connections, connect(multibody_spring.spring2d.flange_b, damper.flange_b))
 
 @named model = ODESystem(connections, t, systems = [world, multibody_spring, root_body, damper])
@@ -100,7 +100,7 @@
 prob = ODEProblem(ssys, defs, (0, 10))
 
 sol = solve(prob, Rodas4(), u0 = prob.u0 .+ 1e-5 .* randn.())
-plot(sol, idxs = multibody_spring.r_rel_0[2], title="Mass-spring-damper without joint")
Example block output

The figure above should look identical to the simulation of the mass-spring-damper further above.

Going 3D

The systems we have modeled so far have all been planar mechanisms. We now extend this to a 3-dimensional system, the Furuta pendulum.

This pendulum, sometimes referred to as a rotary pendulum, has two joints, one in the "shoulder", which is typically configured to rotate around the gravitational axis, and one in the "elbow", which is typically configured to rotate around the axis of the upper arm. The upper arm is attached to the shoulder joint, and the lower arm is attached to the elbow joint. The tip of the pendulum is attached to the lower arm.

using ModelingToolkit, Multibody, JuliaSimCompiler, OrdinaryDiffEq, Plots
+plot(sol, idxs = multibody_spring.r_rel_0[2], title="Mass-spring-damper without joint")
Example block output

The figure above should look identical to the simulation of the mass-spring-damper further above.

Going 3D

The systems we have modeled so far have all been planar mechanisms. We now extend this to a 3-dimensional system, the Furuta pendulum.

This pendulum, sometimes referred to as a rotary pendulum, has two joints, one in the "shoulder", which is typically configured to rotate around the gravitational axis, and one in the "elbow", which is typically configured to rotate around the axis of the upper arm. The upper arm is attached to the shoulder joint, and the lower arm is attached to the elbow joint. The tip of the pendulum is attached to the lower arm.

using ModelingToolkit, Multibody, JuliaSimCompiler, OrdinaryDiffEq, Plots
 import ModelingToolkitStandardLibrary.Mechanical.Rotational.Damper as RDamper
 import Multibody.Rotations
 
@@ -138,7 +138,7 @@
 
 prob = ODEProblem(ssys, [model.shoulder_joint.phi => 0.0, model.elbow_joint.phi => 0.1], (0, 10))
 sol = solve(prob, Rodas4())
-plot(sol, layout=4)
Example block output

In the animation below, we visualize the path that the origin of the pendulum tip traces by providing the tip frame in a vector of frames passed to traces

import GLMakie
+plot(sol, layout=4)
Example block output

In the animation below, we visualize the path that the origin of the pendulum tip traces by providing the tip frame in a vector of frames passed to traces

import GLMakie
 Multibody.render(model, sol, filename = "furuta.gif", traces=[model.tip.frame_a])

furuta

Orientations and directions

Let's break down how to think about directions and orientations when building 3D mechanisms. In the example above, we started with the shoulder joint, this joint rotated around the gravitational axis, n = [0, 1, 0]. When this joint is positioned in joint coordinate shoulder_joint.phi = 0, its frame_a and frame_b will coincide. When the joint rotates, frame_b will rotate around the axis n of frame_a. The frame_a of the joint is attached to the world, so the joint will rotate around the world's y-axis:

get_rot(sol, model.shoulder_joint.frame_b, 0)
3×3 RotMatrix3{Float64} with indices SOneTo(3)×SOneTo(3):
  1.0  0.0  0.0
  0.0  1.0  0.0
@@ -231,7 +231,7 @@
 ssys = structural_simplify(multibody(model))
 prob = ODEProblem(ssys, [model.cartpole.prismatic.s => 0.0, model.cartpole.revolute.phi => 0.1], (0, 10))
 sol = solve(prob, Tsit5())
-plot(sol, layout=4)
Example block output

As usual, we render the simulation in 3D to get a better feel for the system:

import GLMakie
+plot(sol, layout=4)
Example block output

As usual, we render the simulation in 3D to get a better feel for the system:

import GLMakie
 Multibody.render(model, sol, filename = "cartpole.gif", traces=[model.cartpole.pendulum.frame_b])

cartpole

Adding feedback

We will attempt to stabilize the pendulum in the upright position by using feedback control. To design the controller, we linearize the model in the upward equilibrium position and design an infinite-horizon LQR controller using ControlSystems.jl. We then connect the controller to the motor on the cart. See also RobustAndOptimalControl.jl: Control design for a pendulum on a cart for a similar example with more detail on the control design.

Linearization

We start by linearizing the model in the upward equilibrium position using the function ModelingToolkit.linearize.

import ModelingToolkit: D_nounits as D
 using LinearAlgebra
 @named cp = Cartpole(use_world = true)
@@ -313,7 +313,7 @@
 sol = solve(prob, Tsit5())
 cp = model.cartpole
 plot(sol, idxs=[cp.prismatic.s, cp.revolute.phi, cp.motor.f.u], layout=3)
-plot!(sol, idxs=model.reference.output.u, sp=1, l=(:black, :dash), legend=:bottomright)
Example block output
Multibody.render(model, sol, filename = "inverted_cartpole.gif", x=1, z=1)

inverted cartpole

Swing up

Below, we add also an energy-based swing-up controller. For more details this kind of swing-up controller, see Part 7: Control of rotary pendulum using Julia: Swing up control (YouTube)

"Compute total energy, kinetic + potential, for a body rotating around the z-axis of the world"
+plot!(sol, idxs=model.reference.output.u, sp=1, l=(:black, :dash), legend=:bottomright)
Example block output
Multibody.render(model, sol, filename = "inverted_cartpole.gif", x=1, z=1)

inverted cartpole

Swing up

Below, we add also an energy-based swing-up controller. For more details this kind of swing-up controller, see Part 7: Control of rotary pendulum using Julia: Swing up control (YouTube)

"Compute total energy, kinetic + potential, for a body rotating around the z-axis of the world"
 function energy(body, w)
     g = GlobalScope(world.g_inner)
     m = body.m
@@ -368,4 +368,4 @@
 sol = solve(prob, Tsit5(), dt = 1e-2, adaptive=false)
 plot(sol, idxs=[cp.prismatic.s, cp.revolute.phi, cp.motor.f.u, model.E], layout=4)
 hline!([0, 2pi], sp=2, l=(:black, :dash), primary=false)
-plot!(sol, idxs=[model.switching_condition], sp=2)
Example block output
Multibody.render(model, sol, filename = "swingup.gif", x=2, z=2)

inverted cartpole

+plot!(sol, idxs=[model.switching_condition], sp=2)Example block output
Multibody.render(model, sol, filename = "swingup.gif", x=2, z=2)

inverted cartpole

diff --git a/dev/examples/prescribed_pose/2f01ba00.svg b/dev/examples/prescribed_pose/2e591505.svg similarity index 91% rename from dev/examples/prescribed_pose/2f01ba00.svg rename to dev/examples/prescribed_pose/2e591505.svg index f3ace23c..0e15a649 100644 --- a/dev/examples/prescribed_pose/2f01ba00.svg +++ b/dev/examples/prescribed_pose/2e591505.svg @@ -1,54 +1,54 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/prescribed_pose/00b54e07.svg b/dev/examples/prescribed_pose/665f198f.svg similarity index 89% rename from dev/examples/prescribed_pose/00b54e07.svg rename to dev/examples/prescribed_pose/665f198f.svg index 5032cba3..75bbbac6 100644 --- a/dev/examples/prescribed_pose/00b54e07.svg +++ b/dev/examples/prescribed_pose/665f198f.svg @@ -1,92 +1,92 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/prescribed_pose/index.html b/dev/examples/prescribed_pose/index.html index d51e0b1d..bad87061 100644 --- a/dev/examples/prescribed_pose/index.html +++ b/dev/examples/prescribed_pose/index.html @@ -142,5 +142,5 @@ ⋅ ⋅ 0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0 ⋅ - ⋅ ⋅ ⋅ ⋅ ⋅ 0

we see that it is all zeros. This means that there are no differential equations at all in the system, only algebraic equations. The solver will thus only solve for the algebraic variables using a nonlinear root finder. In general, prescribing the value of some state variables removes the need for the solver to solve for them, which can be useful for simplifying simulations. Using the "simulation" above, we can use the solution object to, e.g., find the compression of the spring and the forces acting on the ground over time.

plot(sol, idxs=[model.excited_suspension.suspension.springdamper.s, -model.excited_suspension.suspension.springdamper.f, model.excited_suspension.wheel.wheeljoint.f_n], labels=["Spring length [m]" "Spring force [N] " "Normal force [N]"], layout=(2,1), sp=[1 2 2])
Example block output

Here, we see that the total spring force and the normal force acting on the ground are not equal, this is due to the spring not applying force only in the vertical direction. We can also compute the slip velocity, the velocity with which the contact between the wheel and the ground is sliding along the ground due to the prescribed motion.

wj = model.excited_suspension.wheel.wheeljoint
-plot(sol, idxs=[wj.v_slip, wj.v_slip_long, wj.v_slip_lat], labels=["Slip velocity magnitude" "Longitudinal slip velocity" "Lateral slip velocity"], ylabel="Velocity [m/s]")
Example block output + ⋅ ⋅ ⋅ ⋅ ⋅ 0

we see that it is all zeros. This means that there are no differential equations at all in the system, only algebraic equations. The solver will thus only solve for the algebraic variables using a nonlinear root finder. In general, prescribing the value of some state variables removes the need for the solver to solve for them, which can be useful for simplifying simulations. Using the "simulation" above, we can use the solution object to, e.g., find the compression of the spring and the forces acting on the ground over time.

plot(sol, idxs=[model.excited_suspension.suspension.springdamper.s, -model.excited_suspension.suspension.springdamper.f, model.excited_suspension.wheel.wheeljoint.f_n], labels=["Spring length [m]" "Spring force [N] " "Normal force [N]"], layout=(2,1), sp=[1 2 2])
Example block output

Here, we see that the total spring force and the normal force acting on the ground are not equal, this is due to the spring not applying force only in the vertical direction. We can also compute the slip velocity, the velocity with which the contact between the wheel and the ground is sliding along the ground due to the prescribed motion.

wj = model.excited_suspension.wheel.wheeljoint
+plot(sol, idxs=[wj.v_slip, wj.v_slip_long, wj.v_slip_lat], labels=["Slip velocity magnitude" "Longitudinal slip velocity" "Lateral slip velocity"], ylabel="Velocity [m/s]")
Example block output diff --git a/dev/examples/quad/2c7779fe.svg b/dev/examples/quad/8580b0de.svg similarity index 98% rename from dev/examples/quad/2c7779fe.svg rename to dev/examples/quad/8580b0de.svg index cc416ec9..3f063708 100644 --- a/dev/examples/quad/2c7779fe.svg +++ b/dev/examples/quad/8580b0de.svg @@ -1,150 +1,150 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/quad/4ecbc3d1.svg b/dev/examples/quad/afd013b0.svg similarity index 95% rename from dev/examples/quad/4ecbc3d1.svg rename to dev/examples/quad/afd013b0.svg index 3e1aba3a..b6b733ea 100644 --- a/dev/examples/quad/4ecbc3d1.svg +++ b/dev/examples/quad/afd013b0.svg @@ -1,158 +1,158 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/quad/index.html b/dev/examples/quad/index.html index 014bc944..82446bf5 100644 --- a/dev/examples/quad/index.html +++ b/dev/examples/quad/index.html @@ -194,7 +194,7 @@ sol = solve(prob, FBDF(autodiff=false), reltol=1e-8, abstol=1e-8) @test SciMLBase.successful_retcode(sol) -plot(sol, idxs=[model.arm1.frame_b.r_0[2], model.arm2.frame_b.r_0[2], model.arm3.frame_b.r_0[2], model.arm4.frame_b.r_0[2]], layout=4, framestyle=:zerolines)Example block output
import GLMakie
+plot(sol, idxs=[model.arm1.frame_b.r_0[2], model.arm2.frame_b.r_0[2], model.arm3.frame_b.r_0[2], model.arm4.frame_b.r_0[2]], layout=4, framestyle=:zerolines)
Example block output
import GLMakie
 render(model, sol, 0:0.1:sol.t[end], x=-3, z=-3, y=-1, lookat=[0,-1,0], show_axis=false, filename="quadrotor.gif", framerate=25)

quadrotor animation

The green arrows in the animation indicate the force applied by the thrusters.

LQR control design

Below, we demonstrate a workflow where the model is linearized and an LQR controller is designed to stabilize the quadrotor. We linearize the model using the function named_ss from ControlSystemsMTK, this gives us a linear statespace model with named inputs and outputs. We then design an LQR controller using the lqr function from ControlSystems.jl. Since lqr operates on the state realization of the system, but ModelingToolkit gives no guaratees about what the state realization will be, we specify the LQR penalty matrix in terms of the outputs using the system output matrix $C$.

using ControlSystemsBase, RobustAndOptimalControl, ControlSystemsMTK
 quad = RotorCraft(closed_loop=false, addload=false)
 quad = complete(quad)
@@ -255,7 +255,7 @@
 prob = ODEProblem(ssys, op, (0, 20))
 sol = solve(prob, FBDF(autodiff=false))
 @test SciMLBase.successful_retcode(sol)
-plot(sol, idxs=[model.arm1.frame_b.r_0[2], model.arm2.frame_b.r_0[2], model.arm3.frame_b.r_0[2], model.arm4.frame_b.r_0[2]], layout=4, framestyle=:zerolines)
Example block output
render(model, sol, 0:0.1:sol.t[end], x=-2, z=-2, y=-1, lookat=[0,-1,0], show_axis=false, filename="quadrotor_lqr.gif", framerate=25)

quadrotor with LQR animation

The observant reader may have noticed that we linearized the quadrotor without the cable-suspended load applied, but we simulated the closed-loop system with the load. Thankfully, the LQR controller is robust enough to stabilize the system despite this large model error. Before being satisfied with the controller, we should perform robustness analysis. Below, we compute sensitivity functions at the plant output and input and plot their sigma plots, as well as simultaneous diskmargins at the plant output and input.

# NOTE: this section is temporarily disabled waiting for improved performance in linearization
+plot(sol, idxs=[model.arm1.frame_b.r_0[2], model.arm2.frame_b.r_0[2], model.arm3.frame_b.r_0[2], model.arm4.frame_b.r_0[2]], layout=4, framestyle=:zerolines)
Example block output
render(model, sol, 0:0.1:sol.t[end], x=-2, z=-2, y=-1, lookat=[0,-1,0], show_axis=false, filename="quadrotor_lqr.gif", framerate=25)

quadrotor with LQR animation

The observant reader may have noticed that we linearized the quadrotor without the cable-suspended load applied, but we simulated the closed-loop system with the load. Thankfully, the LQR controller is robust enough to stabilize the system despite this large model error. Before being satisfied with the controller, we should perform robustness analysis. Below, we compute sensitivity functions at the plant output and input and plot their sigma plots, as well as simultaneous diskmargins at the plant output and input.

# NOTE: this section is temporarily disabled waiting for improved performance in linearization
 
 # linop = merge(op, Dict([
 #     collect(model.system_outputs.u) .=> 0
@@ -291,4 +291,4 @@
 #     fig_dm,
 #     layout=(2,3), size=(800,500), legend=:bottomright, ylims=(1e-4, Inf),
 # )
-nothing

While gain and phase margins appear to be reasonable, we have a large high-frequency gain in the transfer functions from measurement noise to control signal, $C(s)S(s)$. For a rotor craft where the control signal manipulates the current through motor windings, this may lead to excessive heat generation in the motors if the sensor measurements are noisy.

The diskmargin at the plant output is small, luckily, the gain variation appears at the plant input where the diskmargin is significantly larger. The diskmargins are visualized in the figure titled "Stable region for combined gain and phase variation". See Diskmargin example to learn more about diskmargins.

+nothing

While gain and phase margins appear to be reasonable, we have a large high-frequency gain in the transfer functions from measurement noise to control signal, $C(s)S(s)$. For a rotor craft where the control signal manipulates the current through motor windings, this may lead to excessive heat generation in the motors if the sensor measurements are noisy.

The diskmargin at the plant output is small, luckily, the gain variation appears at the plant input where the diskmargin is significantly larger. The diskmargins are visualized in the figure titled "Stable region for combined gain and phase variation". See Diskmargin example to learn more about diskmargins.

diff --git a/dev/examples/robot/40d12378.svg b/dev/examples/robot/65cbf37f.svg similarity index 91% rename from dev/examples/robot/40d12378.svg rename to dev/examples/robot/65cbf37f.svg index 606e6e4e..849cdbd9 100644 --- a/dev/examples/robot/40d12378.svg +++ b/dev/examples/robot/65cbf37f.svg @@ -1,424 +1,424 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/robot/7e13e637.svg b/dev/examples/robot/83b5c6f6.svg similarity index 92% rename from dev/examples/robot/7e13e637.svg rename to dev/examples/robot/83b5c6f6.svg index 1ce3d8de..fa0a3783 100644 --- a/dev/examples/robot/7e13e637.svg +++ b/dev/examples/robot/83b5c6f6.svg @@ -1,112 +1,112 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/robot/5740da77.svg b/dev/examples/robot/e47df0e2.svg similarity index 91% rename from dev/examples/robot/5740da77.svg rename to dev/examples/robot/e47df0e2.svg index 18d02dd1..9fd2c8f3 100644 --- a/dev/examples/robot/5740da77.svg +++ b/dev/examples/robot/e47df0e2.svg @@ -1,112 +1,112 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/robot/index.html b/dev/examples/robot/index.html index 745588a4..f3f70dda 100644 --- a/dev/examples/robot/index.html +++ b/dev/examples/robot/index.html @@ -72,7 +72,7 @@ robot.axis5.controller.feedback1.output.u robot.axis6.controller.feedback1.output.u ], sp=7:12, lab="Position error", link=:x) -plot!(xlabel=[fill("", 1, 9) fill("Time [s]", 1, 3)])Example block output

We see that after an initial transient, the robot controller converges to tracking the reference trajectory well. However, since the first three axes of the robot are modeled as slightly flexible, and we are ultimately interested in the tracking performance after the gear box and any flexibilities it may suffer from, we plot also this tracking error

plot(sol, idxs = [
+plot!(xlabel=[fill("", 1, 9) fill("Time [s]", 1, 3)])
Example block output

We see that after an initial transient, the robot controller converges to tracking the reference trajectory well. However, since the first three axes of the robot are modeled as slightly flexible, and we are ultimately interested in the tracking performance after the gear box and any flexibilities it may suffer from, we plot also this tracking error

plot(sol, idxs = [
     robot.axis1.controller.feedback1.output.u / ( -105)
     robot.axis2.controller.feedback1.output.u / (210)
     robot.axis3.controller.feedback1.output.u / (60)
@@ -81,11 +81,11 @@
             robot.pathPlanning.controlBus.axisControlBus1.angle_ref - robot.mechanics.r1.phi #
             robot.pathPlanning.controlBus.axisControlBus2.angle_ref - robot.mechanics.r2.phi #
             robot.pathPlanning.controlBus.axisControlBus3.angle_ref - robot.mechanics.r3.phi #
-], lab="Position error, arm side")
Example block output

Trajectory planning

In the example, the robot is tracking a reference trajectory generated using the function point_to_point and interfaced from the component KinematicPTP. We can inspect the generated trajectory by plotting the positions, velocities and accelerations (we show one joint only to keep the plot size limited):

plot(sol, idxs = [
+], lab="Position error, arm side")
Example block output

Trajectory planning

In the example, the robot is tracking a reference trajectory generated using the function point_to_point and interfaced from the component KinematicPTP. We can inspect the generated trajectory by plotting the positions, velocities and accelerations (we show one joint only to keep the plot size limited):

plot(sol, idxs = [
             robot.pathPlanning.controlBus.axisControlBus1.angle_ref
             robot.pathPlanning.controlBus.axisControlBus1.speed_ref
             robot.pathPlanning.controlBus.axisControlBus1.acceleration_ref
-], layout=(3,1), lab=["\$q\$" "\$\\dot q\$" "\$\\ddot q\$"], xlabel=["" "" "Time [s]"])
Example block output

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
+], layout=(3,1), lab=["\$q\$" "\$\\dot q\$" "\$\\ddot q\$"], xlabel=["" "" "Time [s]"])
Example block output

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
 Multibody.render(robot, sol; y=2, lookat=[0,1,0], filename = "robot.gif")
[ Info: Loading shape mesh /home/runner/work/Multibody.jl/Multibody.jl/src/robot/../../examples/resources/b0.stl
 [ Info: Loading shape mesh /home/runner/work/Multibody.jl/Multibody.jl/src/robot/../../examples/resources/b1.stl
 [ Info: Loading shape mesh /home/runner/work/Multibody.jl/Multibody.jl/src/robot/../../examples/resources/b2.stl
@@ -107,4 +107,4 @@
 fkine(prob.u0, prob.p, 0)
3-element Vector{Float64}:
  -0.986525726789405
   1.3512404737372552
-  0.5695708939243542
Note

The function fkine above takes the full state of the robot model, as opposed to only the joint angles.

+ 0.5695708939243542
Note

The function fkine above takes the full state of the robot model, as opposed to only the joint angles.

diff --git a/dev/examples/ropes_and_cables/index.html b/dev/examples/ropes_and_cables/index.html index 50bce58c..41d182bc 100644 --- a/dev/examples/ropes_and_cables/index.html +++ b/dev/examples/ropes_and_cables/index.html @@ -74,4 +74,4 @@ Multibody.render(mounted_chain, sol, x=3, filename = "mounted_chain.gif") # May take long time for n>=10
("mounted_chain.gif", LScene(), Scene (600px, 450px):
   0 Plots
   1 Child Scene:
-    └ Scene (600px, 450px))

mounted_chain animation

+ └ Scene (600px, 450px))

mounted_chain animation

diff --git a/dev/examples/sensors/59b51a10.svg b/dev/examples/sensors/31f4b3ea.svg similarity index 93% rename from dev/examples/sensors/59b51a10.svg rename to dev/examples/sensors/31f4b3ea.svg index fa141fe7..54cd0beb 100644 --- a/dev/examples/sensors/59b51a10.svg +++ b/dev/examples/sensors/31f4b3ea.svg @@ -1,58 +1,58 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/sensors/index.html b/dev/examples/sensors/index.html index 06c6bf8e..fcc20e56 100644 --- a/dev/examples/sensors/index.html +++ b/dev/examples/sensors/index.html @@ -36,4 +36,4 @@ sol = solve(prob, Rodas4()) @assert SciMLBase.successful_retcode(sol) -plot(sol, idxs = [collect(forcesensor.force.u); collect(joint.frame_a.f)])Example block output

Note how the force sensor measures a force that appears to equal the cut-force in the joint in magnitude, but the orientation appears to differ. Frame cut forces and toques are resolved in the world frame by default, while the force sensor measures the force in the frame of the sensor. We can choose which frame to resolve the measurements in by using hte keyword argument @named forcesensor = CutForce(; resolve_frame = :world). If we do this, the traces in the plot above will overlap.

Since the torque sensor measures a torque in a revolute joint, it should measure zero torque in this case, no torque is transmitted through the revolute joint since the rotational axis is perpendicular to the gravitational force:

all(x -> abs(x) < 1e-3, reduce(hcat, sol[torquesensor.torque.u]))
true
+plot(sol, idxs = [collect(forcesensor.force.u); collect(joint.frame_a.f)])Example block output

Note how the force sensor measures a force that appears to equal the cut-force in the joint in magnitude, but the orientation appears to differ. Frame cut forces and toques are resolved in the world frame by default, while the force sensor measures the force in the frame of the sensor. We can choose which frame to resolve the measurements in by using hte keyword argument @named forcesensor = CutForce(; resolve_frame = :world). If we do this, the traces in the plot above will overlap.

Since the torque sensor measures a torque in a revolute joint, it should measure zero torque in this case, no torque is transmitted through the revolute joint since the rotational axis is perpendicular to the gravitational force:

all(x -> abs(x) < 1e-3, reduce(hcat, sol[torquesensor.torque.u]))
true
diff --git a/dev/examples/space/1aa89ae7.svg b/dev/examples/space/312ba417.svg similarity index 96% rename from dev/examples/space/1aa89ae7.svg rename to dev/examples/space/312ba417.svg index d523a161..88d01194 100644 --- a/dev/examples/space/1aa89ae7.svg +++ b/dev/examples/space/312ba417.svg @@ -1,94 +1,94 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/space/index.html b/dev/examples/space/index.html index 1bf45844..1f204e0b 100644 --- a/dev/examples/space/index.html +++ b/dev/examples/space/index.html @@ -43,5 +43,5 @@ ] prob = ODEProblem(ssys, defs, (0, 5)) sol = solve(prob, Rodas4()) -plot(sol)Example block output
import GLMakie
-Multibody.render(model, sol, filename = "space.gif")

space

Turning gravity off

To simulate without gravity, or with a gravity corresponding to that of another celestial body, set the value of $g$ or $\mu$ appropriately for the chosen gravity model.

+plot(sol)Example block output
import GLMakie
+Multibody.render(model, sol, filename = "space.gif")

space

Turning gravity off

To simulate without gravity, or with a gravity corresponding to that of another celestial body, set the value of $g$ or $\mu$ appropriately for the chosen gravity model.

diff --git a/dev/examples/spherical_pendulum/4f0db690.svg b/dev/examples/spherical_pendulum/0fe440b3.svg similarity index 93% rename from dev/examples/spherical_pendulum/4f0db690.svg rename to dev/examples/spherical_pendulum/0fe440b3.svg index f1bd056b..af07686d 100644 --- a/dev/examples/spherical_pendulum/4f0db690.svg +++ b/dev/examples/spherical_pendulum/0fe440b3.svg @@ -1,50 +1,50 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/spherical_pendulum/index.html b/dev/examples/spherical_pendulum/index.html index 96b83e21..0125bab7 100644 --- a/dev/examples/spherical_pendulum/index.html +++ b/dev/examples/spherical_pendulum/index.html @@ -28,5 +28,5 @@ sol = solve(prob, Rodas4()) @assert SciMLBase.successful_retcode(sol) -plot(sol, idxs = [body.r_0...])Example block output

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
-Multibody.render(model, sol; filename = "spherical.gif") # Use "spherical.mp4" for a video file

animation

+plot(sol, idxs = [body.r_0...])Example block output

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
+Multibody.render(model, sol; filename = "spherical.gif") # Use "spherical.mp4" for a video file

animation

diff --git a/dev/examples/spring_damper_system/e05864c5.svg b/dev/examples/spring_damper_system/a0059dcd.svg similarity index 91% rename from dev/examples/spring_damper_system/e05864c5.svg rename to dev/examples/spring_damper_system/a0059dcd.svg index 4ba10b3a..8040727c 100644 --- a/dev/examples/spring_damper_system/e05864c5.svg +++ b/dev/examples/spring_damper_system/a0059dcd.svg @@ -1,130 +1,130 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/spring_damper_system/index.html b/dev/examples/spring_damper_system/index.html index 95683345..74bd9a41 100644 --- a/dev/examples/spring_damper_system/index.html +++ b/dev/examples/spring_damper_system/index.html @@ -58,5 +58,5 @@ Plots.plot(sol, idxs = [spring1.s, spring2.s]), Plots.plot(sol, idxs = [body1.r_0[2], body2.r_0[2]]), Plots.plot(sol, idxs = [spring1.f, spring2.f]), -)Example block output

In this example we used separate springs and dampers, see also the component SpringDamperParallel which combines the two in one component.

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
-Multibody.render(model, sol; filename = "springdamper.gif")

animation

+)Example block output

In this example we used separate springs and dampers, see also the component SpringDamperParallel which combines the two in one component.

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
+Multibody.render(model, sol; filename = "springdamper.gif")

animation

diff --git a/dev/examples/spring_mass_system/ceba65f3.svg b/dev/examples/spring_mass_system/44e04024.svg similarity index 90% rename from dev/examples/spring_mass_system/ceba65f3.svg rename to dev/examples/spring_mass_system/44e04024.svg index c884da23..9feeaee8 100644 --- a/dev/examples/spring_mass_system/ceba65f3.svg +++ b/dev/examples/spring_mass_system/44e04024.svg @@ -1,54 +1,54 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/spring_mass_system/index.html b/dev/examples/spring_mass_system/index.html index be706a93..7ba922b0 100644 --- a/dev/examples/spring_mass_system/index.html +++ b/dev/examples/spring_mass_system/index.html @@ -41,5 +41,5 @@ sol = solve(prob, Rodas4()) @assert SciMLBase.successful_retcode(sol) -Plots.plot(sol, idxs = [body1.r_0[2], body2.r_0[2]])Example block output

The plot indicates that the two systems behave identically.

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
-Multibody.render(model, sol; x=5, z = 5, filename = "springmass.gif")

animation

You may notice that only one of the springs is rendered, the multibody spring is a 3D mechanical component with a rendering defined, while the spring from the 1D rotational library has no notion of 3D extent.

+Plots.plot(sol, idxs = [body1.r_0[2], body2.r_0[2]])Example block output

The plot indicates that the two systems behave identically.

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
+Multibody.render(model, sol; x=5, z = 5, filename = "springmass.gif")

animation

You may notice that only one of the springs is rendered, the multibody spring is a 3D mechanical component with a rendering defined, while the spring from the 1D rotational library has no notion of 3D extent.

diff --git a/dev/examples/suspension/index.html b/dev/examples/suspension/index.html index b28eb3bf..8bfeaf58 100644 --- a/dev/examples/suspension/index.html +++ b/dev/examples/suspension/index.html @@ -466,4 +466,4 @@ # plot(sol, layout=length(unknowns(ssys)), size=(1900,1200)) import GLMakie -Multibody.render(model, sol, show_axis=false, x=-1.5, y=0.7, z=0.15, lookat=[0,0.1,0.0], timescale=1, filename="suspension_fullcar_wheels.gif") # Video
simplification: 16.786414 seconds (83.48 M allocations: 4.747 GiB, 7.43% gc time, 0.42% compilation time)

suspension with 4 wheels

+Multibody.render(model, sol, show_axis=false, x=-1.5, y=0.7, z=0.15, lookat=[0,0.1,0.0], timescale=1, filename="suspension_fullcar_wheels.gif") # Video
simplification: 18.552082 seconds (83.48 M allocations: 4.747 GiB, 8.09% gc time, 0.43% compilation time)

suspension with 4 wheels

diff --git a/dev/examples/swing/5a65a99e.svg b/dev/examples/swing/d0862839.svg similarity index 99% rename from dev/examples/swing/5a65a99e.svg rename to dev/examples/swing/d0862839.svg index b1818d09..255ab4aa 100644 --- a/dev/examples/swing/5a65a99e.svg +++ b/dev/examples/swing/d0862839.svg @@ -1,52 +1,52 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/swing/index.html b/dev/examples/swing/index.html index c36a2284..19bc40e2 100644 --- a/dev/examples/swing/index.html +++ b/dev/examples/swing/index.html @@ -127,5 +127,5 @@ @time sol = solve(prob, ImplicitEuler(autodiff=false), reltol=1e-2) @assert SciMLBase.successful_retcode(sol) -Plots.plot(sol, idxs = [collect(model.body.r_0);])Example block output
import GLMakie
-Multibody.render(model, sol; y = -1, z = -3, lookat = [0, -1, 0], filename = "swing.gif") # Use "swing.mp4" for a video file

animation

There is an initial transient in the beginning where the springs in the ropes are vibrating substantially, but after about a second this transient is damped out (thanks in part to the, in this case desired, numerical damping contributed by the implicit Euler solver).

+Plots.plot(sol, idxs = [collect(model.body.r_0);])Example block output
import GLMakie
+Multibody.render(model, sol; y = -1, z = -3, lookat = [0, -1, 0], filename = "swing.gif") # Use "swing.mp4" for a video file

animation

There is an initial transient in the beginning where the springs in the ropes are vibrating substantially, but after about a second this transient is damped out (thanks in part to the, in this case desired, numerical damping contributed by the implicit Euler solver).

diff --git a/dev/examples/three_springs/7e46880f.svg b/dev/examples/three_springs/22f191f1.svg similarity index 93% rename from dev/examples/three_springs/7e46880f.svg rename to dev/examples/three_springs/22f191f1.svg index 55dc7afc..47ad4827 100644 --- a/dev/examples/three_springs/7e46880f.svg +++ b/dev/examples/three_springs/22f191f1.svg @@ -1,56 +1,56 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/three_springs/index.html b/dev/examples/three_springs/index.html index 54989cb3..c5737a36 100644 --- a/dev/examples/three_springs/index.html +++ b/dev/examples/three_springs/index.html @@ -34,5 +34,5 @@ sol = solve(prob, Rodas4()) @assert SciMLBase.successful_retcode(sol) -Plots.plot(sol, idxs = [body1.r_0...])Example block output

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
-Multibody.render(model, sol; filename = "three_springs.gif") # Use "three_springs.mp4" for a video file

animation

+Plots.plot(sol, idxs = [body1.r_0...])Example block output

3D animation

Multibody.jl supports automatic 3D rendering of mechanisms, we use this feature to illustrate the result of the simulation below:

import GLMakie
+Multibody.render(model, sol; filename = "three_springs.gif") # Use "three_springs.mp4" for a video file

animation

diff --git a/dev/examples/wheel/98e12431.svg b/dev/examples/wheel/6f13c721.svg similarity index 95% rename from dev/examples/wheel/98e12431.svg rename to dev/examples/wheel/6f13c721.svg index 3796a76d..72546e79 100644 --- a/dev/examples/wheel/98e12431.svg +++ b/dev/examples/wheel/6f13c721.svg @@ -1,37 +1,37 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/dev/examples/wheel/db3c5150.svg b/dev/examples/wheel/d49d9ee8.svg similarity index 89% rename from dev/examples/wheel/db3c5150.svg rename to dev/examples/wheel/d49d9ee8.svg index 332335f4..cd562e08 100644 --- a/dev/examples/wheel/db3c5150.svg +++ b/dev/examples/wheel/d49d9ee8.svg @@ -1,138 +1,138 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/examples/wheel/index.html b/dev/examples/wheel/index.html index 8e444aef..15bd65d8 100644 --- a/dev/examples/wheel/index.html +++ b/dev/examples/wheel/index.html @@ -56,7 +56,7 @@ yticks = ((mu_A, mu_S), ["\$\\mu_{adhesion}\$", "\$\\mu_{slide}\$"]), framestyle = :zerolines, legend = false, -)Example block output

The longitudinal force on the tire is given by

\[f_{long} = - f_n \dfrac{\mu(v_{Slip})}{v_{Slip}} v_{SlipLong}\]

where f_n is the normal force on the tire, μ is the friction coefficient from the slip model, and v_{Slip} is the magnitude of the slip velocity.

The slip velocity is defined such that when the wheel is moving with positive velocity and increasing in speed (accelerating), the slip velocity is negative, i.e., the contact patch is moving slightly backwards. When the wheel is moving with positive velocity and decreasing in speed (braking), the slip velocity is positive, i.e., the contact patch is moving slightly forwards.

@mtkmodel SlipWheelInWorld begin
+)
Example block output

The longitudinal force on the tire is given by

\[f_{long} = - f_n \dfrac{\mu(v_{Slip})}{v_{Slip}} v_{SlipLong}\]

where f_n is the normal force on the tire, μ is the friction coefficient from the slip model, and v_{Slip} is the magnitude of the slip velocity.

The slip velocity is defined such that when the wheel is moving with positive velocity and increasing in speed (accelerating), the slip velocity is negative, i.e., the contact patch is moving slightly backwards. When the wheel is moving with positive velocity and decreasing in speed (braking), the slip velocity is positive, i.e., the contact patch is moving slightly forwards.

@mtkmodel SlipWheelInWorld begin
     @components begin
         world = World()
         wheel = SlippingWheel(
@@ -268,7 +268,7 @@
     model.slipBasedWheelJoint.v_long
     model.slipBasedWheelJoint.v_slip_long
     model.slipBasedWheelJoint.f_long
-], layout=4)
Example block output

Planar two-track model

A more elaborate example with 4 wheels.

@mtkmodel TwoTrackWithDifferentialGear begin
+], layout=4)
Example block output

Planar two-track model

A more elaborate example with 4 wheels.

@mtkmodel TwoTrackWithDifferentialGear begin
     @components begin
         body = Pl.Body(m = 100, I = 1, gy = 0)
         body1 = Pl.Body(m = 300, I = 0.1, r = [1, 1], v = [0, 0], phi = 0, w = 0, gy = 0,)
@@ -378,4 +378,4 @@
 prob = ODEProblem(ssys, defs, (0.0, 5.0))
 sol = solve(prob, Rodas5P(autodiff=false))
 @test SciMLBase.successful_retcode(sol)
-Multibody.render(model, sol, show_axis=false, x=0, y=0, z=5, filename="twotrack.gif", cache=false)

twotrack animation ```

+Multibody.render(model, sol, show_axis=false, x=0, y=0, z=5, filename="twotrack.gif", cache=false)

twotrack animation ```

diff --git a/dev/forces/index.html b/dev/forces/index.html index 1e180528..b3b41f0a 100644 --- a/dev/forces/index.html +++ b/dev/forces/index.html @@ -1,2 +1,2 @@ -Forces · Multibody.jl

Forces

Multibody.DamperMethod
Damper(; d, name, kwargs)

Linear damper acting as line force between frame_a and frame_b. A force f is exerted on the origin of frame_b and with opposite sign on the origin of frame_a along the line from the origin of frame_a to the origin of frame_b according to the equation:

\[f = d D(s)\]

where d is the (viscous) damping parameter, s is the distance between the origin of frame_a and the origin of frame_b and D(s) is the time derivative of s.

Arguments:

  • d: Damping coefficient

Rendering

  • radius = 0.1: Radius of damper when rendered
  • length_fraction = 0.2: Fraction of the length of the damper that is rendered
  • color = [0.5, 0.5, 0.5, 1]: Color of the damper when rendered

See also SpringDamperParallel

source
Multibody.ForceMethod
Force(; name, resolve_frame = :frame_b)

Force acting between two frames, defined by 3 input signals and resolved in frame world, frame_a, frame_b (default)

Connectors:

  • frame_a
  • frame_b
  • force: Of type Blocks.RealInput(3). x-, y-, z-coordinates of force resolved in frame defined by resolve_frame.

Keyword arguments:

  • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_b, options include :frame_a and :world.
source
Multibody.SpringMethod
Spring(; c, name, m = 0, lengthfraction = 0.5, s_unstretched = 0, kwargs)

Linear spring acting as line force between frame_a and frame_b. A force f is exerted on the origin of frame_b and with opposite sign on the origin of frame_a along the line from the origin of frame_a to the origin of frame_b according to the equation:

\[f = c s\]

where c is the spring stiffness parameter, s is the distance between the origin of frame_a and the origin of frame_b.

Optionally, the mass of the spring is taken into account by a point mass located on the line between frame_a and frame_b (default: middle of the line). If the spring mass is zero, the additional equations to handle the mass are removed.

Arguments:

  • c: Spring stiffness
  • m: Mass of the spring (can be zero)
  • lengthfraction: Location of spring mass with respect to frame_a as a fraction of the distance from frame_a to frame_b (=0: at frame_a; =1: at frame_b)
  • s_unstretched: Length of the spring when it is unstretched
  • kwargs: are passed to LineForceWithMass

Rendering

  • num_windings = 6: Number of windings of the coil when rendered
  • color = [0,0,1,1]: Color of the spring when rendered
  • radius = 0.1: Radius of spring when rendered
  • N = 200: Number of points in mesh when rendered. Rendering time can be reduced somewhat by reducing this number.

See also SpringDamperParallel

source
Multibody.SpringDamperParallelMethod
SpringDamperParallel(; name, c, d, s_unstretched)

Linear spring and linear damper in parallel acting as line force between frame_a and frame_b. A force f is exerted on the origin of frame_b and with opposite sign on the origin of frame_a along the line from the origin of frame_a to the origin of frame_b according to the equation:

\[f = c (s - s_{unstretched}) + d \cdot D(s)\]

where c, s_unstretched and d are parameters, s is the distance between the origin of frame_a and the origin of frame_b and D(s) is the time derivative of s.

source
Multibody.TorqueMethod
Torque(; name, resolve_frame = :frame_b)

Torque acting between two frames, defined by 3 input signals and resolved in frame world, frame_a, frame_b (default)

Connectors:

  • frame_a
  • frame_b
  • torque: Of type Blocks.RealInput(3). x-, y-, z-coordinates of torque resolved in frame defined by resolve_frame.

Keyword arguments:

  • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_b, options include :frame_a and :world.
source
Multibody.WorldForceMethod
WorldForce(; name, resolve_frame = :world)

External force acting at frame_b, defined by 3 input signals and resolved in frame :world or :frame_b.

Connectors:

  • frame_b: Frame at which the force is acting
  • force: Of type Blocks.RealInput(3). x-, y-, z-coordinates of force resolved in frame defined by resolve_frame.

Rendering options

  • scale = 0.1: scaling factor for the force [m/N]
  • color = [0,1,0,0.5]: color of the force arrow in rendering
  • radius = 0.05: radius of the force arrow in rendering
source
Multibody.WorldTorqueMethod
WorldTorque(; name, resolve_frame = :world)

External torque acting at frame_b, defined by 3 input signals and resolved in frame :world or :frame_b.

Connectors:

  • frame_b: Frame at which the torque is acting
  • torque: Of type Blocks.RealInput(3). x-, y-, z-coordinates of torque resolved in frame defined by resolve_frame.

Rendering options

  • scale = 0.1: scaling factor for the force [m/N]
  • color = [0,1,0,0.5]: color of the force arrow in rendering
  • radius = 0.05: radius of the force arrow in rendering
source
+Forces · Multibody.jl

Forces

Multibody.DamperMethod
Damper(; d, name, kwargs)

Linear damper acting as line force between frame_a and frame_b. A force f is exerted on the origin of frame_b and with opposite sign on the origin of frame_a along the line from the origin of frame_a to the origin of frame_b according to the equation:

\[f = d D(s)\]

where d is the (viscous) damping parameter, s is the distance between the origin of frame_a and the origin of frame_b and D(s) is the time derivative of s.

Arguments:

  • d: Damping coefficient

Rendering

  • radius = 0.1: Radius of damper when rendered
  • length_fraction = 0.2: Fraction of the length of the damper that is rendered
  • color = [0.5, 0.5, 0.5, 1]: Color of the damper when rendered

See also SpringDamperParallel

source
Multibody.ForceMethod
Force(; name, resolve_frame = :frame_b)

Force acting between two frames, defined by 3 input signals and resolved in frame world, frame_a, frame_b (default)

Connectors:

  • frame_a
  • frame_b
  • force: Of type Blocks.RealInput(3). x-, y-, z-coordinates of force resolved in frame defined by resolve_frame.

Keyword arguments:

  • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_b, options include :frame_a and :world.
source
Multibody.SpringMethod
Spring(; c, name, m = 0, lengthfraction = 0.5, s_unstretched = 0, kwargs)

Linear spring acting as line force between frame_a and frame_b. A force f is exerted on the origin of frame_b and with opposite sign on the origin of frame_a along the line from the origin of frame_a to the origin of frame_b according to the equation:

\[f = c s\]

where c is the spring stiffness parameter, s is the distance between the origin of frame_a and the origin of frame_b.

Optionally, the mass of the spring is taken into account by a point mass located on the line between frame_a and frame_b (default: middle of the line). If the spring mass is zero, the additional equations to handle the mass are removed.

Arguments:

  • c: Spring stiffness
  • m: Mass of the spring (can be zero)
  • lengthfraction: Location of spring mass with respect to frame_a as a fraction of the distance from frame_a to frame_b (=0: at frame_a; =1: at frame_b)
  • s_unstretched: Length of the spring when it is unstretched
  • kwargs: are passed to LineForceWithMass

Rendering

  • num_windings = 6: Number of windings of the coil when rendered
  • color = [0,0,1,1]: Color of the spring when rendered
  • radius = 0.1: Radius of spring when rendered
  • N = 200: Number of points in mesh when rendered. Rendering time can be reduced somewhat by reducing this number.

See also SpringDamperParallel

source
Multibody.SpringDamperParallelMethod
SpringDamperParallel(; name, c, d, s_unstretched)

Linear spring and linear damper in parallel acting as line force between frame_a and frame_b. A force f is exerted on the origin of frame_b and with opposite sign on the origin of frame_a along the line from the origin of frame_a to the origin of frame_b according to the equation:

\[f = c (s - s_{unstretched}) + d \cdot D(s)\]

where c, s_unstretched and d are parameters, s is the distance between the origin of frame_a and the origin of frame_b and D(s) is the time derivative of s.

source
Multibody.TorqueMethod
Torque(; name, resolve_frame = :frame_b)

Torque acting between two frames, defined by 3 input signals and resolved in frame world, frame_a, frame_b (default)

Connectors:

  • frame_a
  • frame_b
  • torque: Of type Blocks.RealInput(3). x-, y-, z-coordinates of torque resolved in frame defined by resolve_frame.

Keyword arguments:

  • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_b, options include :frame_a and :world.
source
Multibody.WorldForceMethod
WorldForce(; name, resolve_frame = :world)

External force acting at frame_b, defined by 3 input signals and resolved in frame :world or :frame_b.

Connectors:

  • frame_b: Frame at which the force is acting
  • force: Of type Blocks.RealInput(3). x-, y-, z-coordinates of force resolved in frame defined by resolve_frame.

Rendering options

  • scale = 0.1: scaling factor for the force [m/N]
  • color = [0,1,0,0.5]: color of the force arrow in rendering
  • radius = 0.05: radius of the force arrow in rendering
source
Multibody.WorldTorqueMethod
WorldTorque(; name, resolve_frame = :world)

External torque acting at frame_b, defined by 3 input signals and resolved in frame :world or :frame_b.

Connectors:

  • frame_b: Frame at which the torque is acting
  • torque: Of type Blocks.RealInput(3). x-, y-, z-coordinates of torque resolved in frame defined by resolve_frame.

Rendering options

  • scale = 0.1: scaling factor for the force [m/N]
  • color = [0,1,0,0.5]: color of the force arrow in rendering
  • radius = 0.05: radius of the force arrow in rendering
source
diff --git a/dev/frames/index.html b/dev/frames/index.html index 40ebfa39..6ef87d31 100644 --- a/dev/frames/index.html +++ b/dev/frames/index.html @@ -1,2 +1,2 @@ -Frames · Multibody.jl

Frames

Docstrings

Multibody.FrameFunction
Frame(; name)

Frame is the fundamental 3D connector in the multibody library. Most components have one or several Frame connectors that can be connected together.

The Frame connector has internal variables for

  • r_0: The position vector from the world frame to the frame origin, resolved in the world frame
  • f: The cut force resolved in the connector frame
  • tau: The cut torque resolved in the connector frame
  • Depending on usage, also rotation and rotational velocity variables, accessed using ori(frame).R and ori(frame).w. The rotation matrix represents the rotation to rotate the world frame into the connector frame

Parameters

  • render = false: If true, the connector is rendered in animations
  • length = 1.0: Length of the frame when rendered
  • radius = 0.1: Radius of the frame when rendered
source
Multibody.PlanarMechanics.PartialTwoFramesConstant
PartialTwoFrames(;name)

Partial model with two frames

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.FrameFunction
Frame(;name)

Coordinate system (2-dim.) fixed to the component with one cut-force and cut-torque. All variables are resolved in the planar world frame.

Variables:

  • x: [m] x position
  • y: [m] y position
  • phi: [rad] rotation angle (counterclockwise)
  • fx: [N] force in the x direction
  • fy: [N] force in the y direction
  • tau: [N.m] torque (clockwise)

Parameters:

  • render: [Bool] Render the joint in animations
  • length: [m] Length of each axis in animations
  • radius: [m] Radius of each axis in animations
source
+Frames · Multibody.jl

Frames

Docstrings

Multibody.FrameFunction
Frame(; name)

Frame is the fundamental 3D connector in the multibody library. Most components have one or several Frame connectors that can be connected together.

The Frame connector has internal variables for

  • r_0: The position vector from the world frame to the frame origin, resolved in the world frame
  • f: The cut force resolved in the connector frame
  • tau: The cut torque resolved in the connector frame
  • Depending on usage, also rotation and rotational velocity variables, accessed using ori(frame).R and ori(frame).w. The rotation matrix represents the rotation to rotate the world frame into the connector frame

Parameters

  • render = false: If true, the connector is rendered in animations
  • length = 1.0: Length of the frame when rendered
  • radius = 0.1: Radius of the frame when rendered
source
Multibody.PlanarMechanics.PartialTwoFramesConstant
PartialTwoFrames(;name)

Partial model with two frames

Connectors:

  • frame_a Frame Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b Frame Coordinate system fixed to the component with one cut-force and cut-torque
source
Multibody.PlanarMechanics.FrameFunction
Frame(;name)

Coordinate system (2-dim.) fixed to the component with one cut-force and cut-torque. All variables are resolved in the planar world frame.

Variables:

  • x: [m] x position
  • y: [m] y position
  • phi: [rad] rotation angle (counterclockwise)
  • fx: [N] force in the x direction
  • fy: [N] force in the y direction
  • tau: [N.m] torque (clockwise)

Parameters:

  • render: [Bool] Render the joint in animations
  • length: [m] Length of each axis in animations
  • radius: [m] Radius of each axis in animations
source
diff --git a/dev/index.html b/dev/index.html index d8476d35..ad9191dc 100644 --- a/dev/index.html +++ b/dev/index.html @@ -67,4 +67,4 @@

Installation

To install this library, first follow the installation instructions for JuliaSimCompiler. In particular, you need to add the JuliaHub Registry.

After the registry is added and JuliaSimCompiler is installed, you may install this package using

import Pkg
-Pkg.add("Multibody")

Notable differences from Modelica

  • The torque variable in Multibody.jl is typically called tau rather than t to not conflict with the often used independent variable t used to denote time.
  • Multibody.jl occasionally requires the user to specify which component should act as the root of the kinematic tree. This only occurs when bodies are connected directly to force components without a joint parallel to the force component.
  • In Multibody.jl, the orientation object of a Frame is accessed using the function ori.
  • Quaternions in Multibody.jl follow the order $[s, i, j, k]$, i.e., scalar/real part first.

2D and 3D modeling

Multibody.jl offers components for modeling in both 2D and 3D. 2D modeling, often referred to as planar mechanics, is a subset of 3D modeling where the motion is constrained to a plane, the x,y plane. Planar mechanics is sometimes referred to as 3 degrees of freedom (DOF) modeling, referring to the 2 translational DOF and one rotational DOF that the plane offers. Most components in Multibody.jl are aimed at 3D modeling (sometimes referred to as 6 DOF modeling), but components for 2D modeling exist in the submodule Multibody.PlanarMechanics.

The components from ModelingToolkitStandardLibrary.Mechanical are 1D, i.e., a single degree of freedom only. These components can be used in both 2D and 3D modeling together with Multibody components that have support for attaching 1D components, such as joints supporting the axisflange keyword.

+Pkg.add("Multibody")

Notable differences from Modelica

  • The torque variable in Multibody.jl is typically called tau rather than t to not conflict with the often used independent variable t used to denote time.
  • Multibody.jl occasionally requires the user to specify which component should act as the root of the kinematic tree. This only occurs when bodies are connected directly to force components without a joint parallel to the force component.
  • In Multibody.jl, the orientation object of a Frame is accessed using the function ori.
  • Quaternions in Multibody.jl follow the order $[s, i, j, k]$, i.e., scalar/real part first.

2D and 3D modeling

Multibody.jl offers components for modeling in both 2D and 3D. 2D modeling, often referred to as planar mechanics, is a subset of 3D modeling where the motion is constrained to a plane, the x,y plane. Planar mechanics is sometimes referred to as 3 degrees of freedom (DOF) modeling, referring to the 2 translational DOF and one rotational DOF that the plane offers. Most components in Multibody.jl are aimed at 3D modeling (sometimes referred to as 6 DOF modeling), but components for 2D modeling exist in the submodule Multibody.PlanarMechanics.

The components from ModelingToolkitStandardLibrary.Mechanical are 1D, i.e., a single degree of freedom only. These components can be used in both 2D and 3D modeling together with Multibody components that have support for attaching 1D components, such as joints supporting the axisflange keyword.

diff --git a/dev/interfaces/index.html b/dev/interfaces/index.html index 83f2bb35..7dbb855c 100644 --- a/dev/interfaces/index.html +++ b/dev/interfaces/index.html @@ -1,2 +1,2 @@ -Interfaces · Multibody.jl +Interfaces · Multibody.jl diff --git a/dev/joints/index.html b/dev/joints/index.html index ebe6a740..1201b833 100644 --- a/dev/joints/index.html +++ b/dev/joints/index.html @@ -1,5 +1,5 @@ -Joints · Multibody.jl

Joints

A joint restricts the number of degrees of freedom (DOF) of a body. For example, a free floating body has 6 DOF, but if it is attached to a Revolute joint, the joint restricts all but one rotational degree of freedom (a revolute joint acts like a hinge). Similarily, a Prismatic joint restricts all but one translational degree of freedom (a prismatic joint acts like a slider).

A Spherical joints restricts all translational degrees of freedom, but allows all rotational degrees of freedom. It thus transmits no torque. A Planar joint moves in a plane, i.e., it restricts one translational DOF and two rotational DOF. A Universal joint has two rotational DOF.

Some joints offer the option to add 1-dimensional components to them by providing the keyword axisflange = true. This allows us to add, e.g., springs, dampers, sensors, and actuators to the joint.

Docstrings

Multibody.PlanarConstant
Planar(; n = [0,0,1], n_x = [1,0,0], cylinderlength = 0.1, cylinderdiameter = 0.05, cylindercolor = [1, 0, 1, 1], boxwidth = 0.3*cylinderdiameter, boxheight = boxwidth, boxcolor = [0, 0, 1, 1])

Joint where frame_b can move in a plane and can rotate around an axis orthogonal to the plane. The plane is defined by vector n which is perpendicular to the plane and by vector n_x, which points in the direction of the x-axis of the plane. frame_a and frame_b coincide when s_x=prismatic_x.s=0, s_y=prismatic_y.s=0 and phi=revolute.phi=0.

Structural parameters

  • n: Axis orthogonal to unconstrained plane, resolved in frame_a (= same as in frame_b)
  • n_x: Vector in direction of x-axis of plane, resolved in frame_a (n_x shall be orthogonal to n)

Connectors

  • frame_a: Frame for the joint
  • frame_b: Frame for the joint

Variables

  • s_x: Relative distance along first prismatic joint starting at frame_a
  • s_y: Relative distance along second prismatic joint starting at first prismatic joint
  • phi: Relative rotation angle from frame_a to frame_b
  • v_x: Relative velocity along first prismatic joint
  • v_y: Relative velocity along second prismatic joint
  • w: Relative angular velocity around revolute joint
  • a_x: Relative acceleration along first prismatic joint
  • a_y: Relative acceleration along second prismatic joint
  • wd: Relative angular acceleration around revolute joint

Rendering parameters

  • cylinderlength: Length of the revolute cylinder
  • cylinderdiameter: Diameter of the revolute cylinder
  • cylindercolor: (structural) Color of the revolute cylinder
  • boxwidth: Width of the prismatic joint boxes
  • boxheight: Height of the prismatic joint boxes
  • boxcolor: (structural) Color of the prismatic joint boxes
  • radius: (structural) Radius of the revolute cylinder
  • render: Enable rendering of the joint in animations
source
Multibody.FreeMotionMethod
FreeMotion(; name, state = true, sequence, isroot = true, w_rel_a_fixed = false, z_rel_a_fixed = false, phi = 0, phid = 0, phidd = 0, w_rel_b = 0, r_rel_a = 0, v_rel_a = 0, a_rel_a = 0)

Joint which does not constrain the motion between frame_a and frame_b. Such a joint is only meaningful if the relative distance and orientation between frame_a and frame_b, and their derivatives, shall be used as state.

Note, that bodies such as Body, BodyShape, have potential state variables describing the distance and orientation, and their derivatives, between the world frame and a body fixed frame. Therefore, if these potential state variables are suited, a FreeMotion joint is not needed.

The state of the FreeMotion object consits of:

The relative position vector r_rel_a from the origin of frame_a to the origin of frame_b, resolved in frame_a and the relative velocity v_rel_a of the origin of frame_b with respect to the origin of frame_a, resolved in frame_a (= D(r_rel_a)).

Arguments

  • state: Enforce this joint having state, this is often desired and is the default choice.
  • sequence: Rotation sequence, defaults to [1, 2, 3]
  • w_rel_a_fixed: = true, if w_rel_a_start are used as initial values, else as guess values
  • z_rel_a_fixed: = true, if z_rel_a_start are used as initial values, else as guess values

Initial condition arguments:

  • phi
  • phid
  • phidd
  • w_rel_b
  • r_rel_a
  • v_rel_a
  • a_rel_a
source
Multibody.GearConstraintMethod
GearConstraint(; name, ratio, checkTotalPower = false, n_a, n_b, r_a, r_b)

This ideal massless joint provides a gear constraint between frames frame_a and frame_b. The axes of rotation of frame_a and frame_b may be arbitrary.

  • ratio: Gear ratio
  • n_a: Axis of rotation of frame_a
  • n_b: Axis of rotation of frame_b
  • r_a: Vector from frame bearing to frame_a resolved in bearing
  • r_b: Vector from frame bearing to frame_b resolved in bearing
source
Multibody.JointRRRMethod
JointRRR(;
+Joints · Multibody.jl

Joints

A joint restricts the number of degrees of freedom (DOF) of a body. For example, a free floating body has 6 DOF, but if it is attached to a Revolute joint, the joint restricts all but one rotational degree of freedom (a revolute joint acts like a hinge). Similarily, a Prismatic joint restricts all but one translational degree of freedom (a prismatic joint acts like a slider).

A Spherical joints restricts all translational degrees of freedom, but allows all rotational degrees of freedom. It thus transmits no torque. A Planar joint moves in a plane, i.e., it restricts one translational DOF and two rotational DOF. A Universal joint has two rotational DOF.

Some joints offer the option to add 1-dimensional components to them by providing the keyword axisflange = true. This allows us to add, e.g., springs, dampers, sensors, and actuators to the joint.

Docstrings

Multibody.PlanarConstant
Planar(; n = [0,0,1], n_x = [1,0,0], cylinderlength = 0.1, cylinderdiameter = 0.05, cylindercolor = [1, 0, 1, 1], boxwidth = 0.3*cylinderdiameter, boxheight = boxwidth, boxcolor = [0, 0, 1, 1])

Joint where frame_b can move in a plane and can rotate around an axis orthogonal to the plane. The plane is defined by vector n which is perpendicular to the plane and by vector n_x, which points in the direction of the x-axis of the plane. frame_a and frame_b coincide when s_x=prismatic_x.s=0, s_y=prismatic_y.s=0 and phi=revolute.phi=0.

Structural parameters

  • n: Axis orthogonal to unconstrained plane, resolved in frame_a (= same as in frame_b)
  • n_x: Vector in direction of x-axis of plane, resolved in frame_a (n_x shall be orthogonal to n)

Connectors

  • frame_a: Frame for the joint
  • frame_b: Frame for the joint

Variables

  • s_x: Relative distance along first prismatic joint starting at frame_a
  • s_y: Relative distance along second prismatic joint starting at first prismatic joint
  • phi: Relative rotation angle from frame_a to frame_b
  • v_x: Relative velocity along first prismatic joint
  • v_y: Relative velocity along second prismatic joint
  • w: Relative angular velocity around revolute joint
  • a_x: Relative acceleration along first prismatic joint
  • a_y: Relative acceleration along second prismatic joint
  • wd: Relative angular acceleration around revolute joint

Rendering parameters

  • cylinderlength: Length of the revolute cylinder
  • cylinderdiameter: Diameter of the revolute cylinder
  • cylindercolor: (structural) Color of the revolute cylinder
  • boxwidth: Width of the prismatic joint boxes
  • boxheight: Height of the prismatic joint boxes
  • boxcolor: (structural) Color of the prismatic joint boxes
  • radius: (structural) Radius of the revolute cylinder
  • render: Enable rendering of the joint in animations
source
Multibody.FreeMotionMethod
FreeMotion(; name, state = true, sequence, isroot = true, w_rel_a_fixed = false, z_rel_a_fixed = false, phi = 0, phid = 0, phidd = 0, w_rel_b = 0, r_rel_a = 0, v_rel_a = 0, a_rel_a = 0)

Joint which does not constrain the motion between frame_a and frame_b. Such a joint is only meaningful if the relative distance and orientation between frame_a and frame_b, and their derivatives, shall be used as state.

Note, that bodies such as Body, BodyShape, have potential state variables describing the distance and orientation, and their derivatives, between the world frame and a body fixed frame. Therefore, if these potential state variables are suited, a FreeMotion joint is not needed.

The state of the FreeMotion object consits of:

The relative position vector r_rel_a from the origin of frame_a to the origin of frame_b, resolved in frame_a and the relative velocity v_rel_a of the origin of frame_b with respect to the origin of frame_a, resolved in frame_a (= D(r_rel_a)).

Arguments

  • state: Enforce this joint having state, this is often desired and is the default choice.
  • sequence: Rotation sequence, defaults to [1, 2, 3]
  • w_rel_a_fixed: = true, if w_rel_a_start are used as initial values, else as guess values
  • z_rel_a_fixed: = true, if z_rel_a_start are used as initial values, else as guess values

Initial condition arguments:

  • phi
  • phid
  • phidd
  • w_rel_b
  • r_rel_a
  • v_rel_a
  • a_rel_a
source
Multibody.GearConstraintMethod
GearConstraint(; name, ratio, checkTotalPower = false, n_a, n_b, r_a, r_b)

This ideal massless joint provides a gear constraint between frames frame_a and frame_b. The axes of rotation of frame_a and frame_b may be arbitrary.

  • ratio: Gear ratio
  • n_a: Axis of rotation of frame_a
  • n_b: Axis of rotation of frame_b
  • r_a: Vector from frame bearing to frame_a resolved in bearing
  • r_b: Vector from frame bearing to frame_b resolved in bearing
source
Multibody.JointRRRMethod
JointRRR(;
     name,
     n_a = [0,0,1],
     n_b = [0,0,1],
@@ -8,7 +8,7 @@
     phi_offset = 0, 
     phi_guess = 0,
 
-)

This component consists of 3 revolute joints with parallel axes of rotation that are connected together by two rods.

This joint aggregation introduces neither constraints nor state variables and should therefore be used in kinematic loops whenever possible to avoid non-linear systems of equations. It is only meaningful to use this component in planar loops. Basically, the position and orientation of the 3 revolute joints as well as of frame_ia, frame_ib, and frame_im are calculated by solving analytically a non-linear equation, given the position and orientation at frame_a and at frame_b.

Connector frame_a is the "left" side of the first revolute joint whereas frame_ia is the "right side of this revolute joint, fixed in rod 1. Connector frame_b is the "right" side of the third revolute joint whereas frame_ib is the "left" side of this revolute joint, fixed in rod 2. Finally, connector frame_im is the connector at the "right" side of the revolute joint in the middle, fixed in rod 2.

The easiest way to define the parameters of this joint is by moving the MultiBody system in a reference configuration where all frames of all components are parallel to each other (alternatively, at least frame_a, frame_ia, frame_im, frame_ib, frame_b of the JointRRR joint should be parallel to each other when defining an instance of this component).

Basically, the JointRRR model internally consists of a universal-spherical-revolute joint aggregation (= JointUSR). In a planar loop this will behave as if 3 revolute joints with parallel axes are connected by rigid rods.

Arguments

  • n_a Axis of revolute joints resolved in frame_a (all axes are parallel to each other)
  • n_b Axis of revolute joint fixed and resolved in frame_b
  • rRod1_ia Vector from origin of frame_a to revolute joint in the middle, resolved in frame_ia
  • rRod2_ib Vector from origin of frame_ib to revolute joint in the middle, resolved in frame_ib
  • phi_offset Relative angle offset of revolute joint at frame_b (angle = phi(t) + phi_offset)

Connectors

  • frame_a: Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b: Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_ia: Coordinate system at origin of frame_a fixed at connecting rod of left and middle revolute joint
  • frame_ib: Coordinate system at origin of frame_ib fixed at connecting rod of middle and right revolute joint
  • frame_im: Coordinate system at origin of revolute joint in the middle fixed at connecting rod of middle and right revolute joint
  • axis: 1-dim. rotational flange that drives the right revolute joint at frame_b
  • bearing: 1-dim. rotational flange of the drive bearing of the right revolute joint at frame_b
source
Multibody.JointUSRMethod
JointUSR(;
+)

This component consists of 3 revolute joints with parallel axes of rotation that are connected together by two rods.

This joint aggregation introduces neither constraints nor state variables and should therefore be used in kinematic loops whenever possible to avoid non-linear systems of equations. It is only meaningful to use this component in planar loops. Basically, the position and orientation of the 3 revolute joints as well as of frame_ia, frame_ib, and frame_im are calculated by solving analytically a non-linear equation, given the position and orientation at frame_a and at frame_b.

Connector frame_a is the "left" side of the first revolute joint whereas frame_ia is the "right side of this revolute joint, fixed in rod 1. Connector frame_b is the "right" side of the third revolute joint whereas frame_ib is the "left" side of this revolute joint, fixed in rod 2. Finally, connector frame_im is the connector at the "right" side of the revolute joint in the middle, fixed in rod 2.

The easiest way to define the parameters of this joint is by moving the MultiBody system in a reference configuration where all frames of all components are parallel to each other (alternatively, at least frame_a, frame_ia, frame_im, frame_ib, frame_b of the JointRRR joint should be parallel to each other when defining an instance of this component).

Basically, the JointRRR model internally consists of a universal-spherical-revolute joint aggregation (= JointUSR). In a planar loop this will behave as if 3 revolute joints with parallel axes are connected by rigid rods.

Arguments

  • n_a Axis of revolute joints resolved in frame_a (all axes are parallel to each other)
  • n_b Axis of revolute joint fixed and resolved in frame_b
  • rRod1_ia Vector from origin of frame_a to revolute joint in the middle, resolved in frame_ia
  • rRod2_ib Vector from origin of frame_ib to revolute joint in the middle, resolved in frame_ib
  • phi_offset Relative angle offset of revolute joint at frame_b (angle = phi(t) + phi_offset)

Connectors

  • frame_a: Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_b: Coordinate system fixed to the component with one cut-force and cut-torque
  • frame_ia: Coordinate system at origin of frame_a fixed at connecting rod of left and middle revolute joint
  • frame_ib: Coordinate system at origin of frame_ib fixed at connecting rod of middle and right revolute joint
  • frame_im: Coordinate system at origin of revolute joint in the middle fixed at connecting rod of middle and right revolute joint
  • axis: 1-dim. rotational flange that drives the right revolute joint at frame_b
  • bearing: 1-dim. rotational flange of the drive bearing of the right revolute joint at frame_b
source
Multibody.JointUSRMethod
JointUSR(;
     name,
     n1_a = [0, 0, 1],
     n_b = [0, 0, 1],
@@ -16,4 +16,4 @@
     rRod1_ib = [-1, 0, 0],
     phi_offset = 0,
     phi_guess = 0,
-)

This component consists of a universal joint at frame_a, a revolute joint at frame_b and a spherical joint which is connected via rod1 to the universal and via rod2 to the revolute joint.

This joint aggregation has no mass and no inertia and introduces neither constraints nor potential state variables. It should be used in kinematic loops whenever possible since the non-linear system of equations introduced by this joint aggregation is solved analytically (i.e., a solution is always computed, if a unique solution exists).

The universal joint is defined in the following way:

  • The rotation axis of revolute joint 1 is along parameter vector n1_a which is fixed in frame_a.
  • The rotation axis of revolute joint 2 is perpendicular to axis 1 and to the line connecting the universal and the spherical joint (= rod 1).

The definition of axis 2 of the universal joint is performed according to the most often occurring case for the sake of simplicity. Otherwise, the treatment is much more complicated and the number of operations is considerably higher, if axis 2 is not orthogonal to axis 1 and to the connecting rod.

Note, there is a singularity when axis 1 and the connecting rod are parallel to each other. Therefore, if possible n1_a should be selected in such a way that it is perpendicular to rRod1_ia in the initial configuration (i.e., the distance to the singularity is as large as possible).

The rest of this joint aggregation is defined by the following parameters:

  • positive_branch: The positive branch of the revolute joint is selected (cf. elbow up vs. elbow down).
  • The position of the spherical joint with respect to the universal joint is defined by vector rRod1_ia. This vector is directed from frame_a to the spherical joint and is resolved in frame_ia (it is most simple to select frame_ia such that it is parallel to frame_a in the reference or initial configuration).
  • The position of the spherical joint with respect to the revolute joint is defined by vector rRod2_ib. This vector is directed from the inner frame of the revolute joint (frame_ib or revolute.frame_a) to the spherical joint and is resolved in frame_ib (note, that frame_ib and frame_b are parallel to each other).
  • The axis of rotation of the revolute joint is defined by axis vector n_b. It is fixed and resolved in frame_b.
  • When specifying this joint aggregation with the definitions above, two different configurations are possible. Via parameter phi_guess a guess value for revolute.phi(t0) at the initial time t0 is given. The configuration is selected that is closest to phi_guess (|revolute.phi - phi_guess| is minimal).

Connectors

  • frame_a: Frame for the universal joint
  • frame_b: Frame for the revolute joint
  • An additional frame_ia is present. It is fixed in the rod connecting the universal and the spherical joint at the origin of frame_a. The placement of frame_ia on the rod is implicitly defined by the universal joint (frame_a and frame_ia coincide when the angles of the two revolute joints of the universal joint are zero) and by parameter vector rRod1ia, the position vector from the origin of `frameato the spherical joint, resolved inframe_ia`.
  • An additional frame_ib is present. It is fixed in the rod connecting the revolute and the spherical joint at the side of the revolute joint that is connected to this rod (= rod2.frame_a = revolute.frame_a).
  • An additional frame_im is present. It is fixed in the rod connecting the revolute and the spherical joint at the side of the spherical joint that is connected to this rod (= rod2.frame_b). It is always parallel to frame_ib.
source
Multibody.PrismaticMethod
Prismatic(; name, n = [0, 0, 1], axisflange = false)

Prismatic joint with 1 translational degree-of-freedom

  • n: The axis of motion (unit vector)
  • axisflange: If true, the joint will have two additional frames from Mechanical.Translational, axis and support, between which translational components such as springs and dampers can be connected.

If axisflange, flange connectors for ModelicaStandardLibrary.Mechanics.TranslationalModelica are also available:

  • axis: 1-dim. translational flange that drives the joint
  • support: 1-dim. translational flange of the drive support (assumed to be fixed in the world frame, NOT in the joint)

The function returns an ODESystem representing the prismatic joint.

source
Multibody.PrismaticConstraintMethod
PrismaticConstraint(; name, color, radius = 0.05, x_locked = true, y_locked = true, z_locked = true, render = true)

This model does not use explicit variables e.g. state variables in order to describe the relative motion of frame_b with respect to frame_a, but defines kinematic constraints between the frame_a and frame_b. The forces and torques at both frames are then evaluated in such a way that the constraints are satisfied. Sometimes this type of formulation is called an implicit joint in literature.

As a consequence of the formulation, the relative kinematics between frame_a and frame_b cannot be initialized.

In complex multibody systems with closed loops this may help to simplify the system of non-linear equations. Compare the simplification result using the classical joint formulation and this alternative formulation to check which one is more efficient for the particular system under consideration.

In systems without closed loops the use of this implicit joint does not make sense or may even be disadvantageous.

Parameters

  • color: Color of the joint in animations (RGBA)
  • radius: Radius of the joint in animations
  • x_locked: Set to false if the translational motion in x-direction shall be free
  • y_locked: Set to false if the translational motion in y-direction shall be free
  • z_locked: Set to false if the translational motion in z-direction shall be free
  • render: Whether or not the joint is rendered in animations
source
Multibody.RevoluteMethod
Revolute(; name, phi0 = 0, w0 = 0, n, axisflange = false)

Revolute joint with 1 rotational degree-of-freedom

  • phi0: Initial angle
  • w0: Iniitial angular velocity
  • n: The axis of rotation
  • axisflange: If true, the joint will have two additional frames from Mechanical.Rotational, axis and support, between which rotational components such as springs and dampers can be connected.

If axisflange, flange connectors for ModelicaStandardLibrary.Mechanics.Rotational are also available:

  • axis: 1-dim. rotational flange that drives the joint
  • support: 1-dim. rotational flange of the drive support (assumed to be fixed in the world frame, NOT in the joint)

Rendering options

  • radius = 0.05: Radius of the joint in animations
  • length = radius: Length of the joint in animations
  • color: Color of the joint in animations, a vector of length 4 with values between [0, 1] providing RGBA values
source
Multibody.RevolutePlanarLoopConstraintMethod
RevolutePlanarLoopConstraint(; name, n)

Revolute joint that is described by 2 positional constraints for usage in a planar loop (the ambiguous cut-force perpendicular to the loop and the ambiguous cut-torques are set arbitrarily to zero)

Joint where frame_b rotates around axis n which is fixed in frame_a and where this joint is used in a planar loop providing 2 constraint equations on position level.

If a planar loop is present, e.g., consisting of 4 revolute joints where the joint axes are all parallel to each other, then there is no unique mathematical solution if all revolute joints are modelled with Revolute and the symbolic algorithms will fail. The reason is that, e.g., the cut-forces in the revolute joints perpendicular to the planar loop are not uniquely defined when 3-dim. descriptions of revolute joints are used. In this case, one revolute joint in the loop has to be replaced by model RevolutePlanarLoopConstraint. The effect is that from the 5 constraints of a 3-dim. revolute joint, 3 constraints are removed and replaced by appropriate known variables (e.g., the force in the direction of the axis of rotation is treated as known with value equal to zero; for standard revolute joints, this force is an unknown quantity).

source
Multibody.SphericalMethod
Spherical(; name, state = false, isroot = true, w_rel_a_fixed = false, z_rel_a_fixed = false, sequence, phi = 0, phid = 0, phidd = 0, d = 0)

Joint with 3 constraints that define that the origin of frame_a and the origin of frame_b coincide. By default this joint defines only the 3 constraints without any potential state variables. If parameter state is set to true, three states are introduced. The orientation of frame_b is computed by rotating frame_a along the axes defined in parameter vector sequence (default = [1,2,3], i.e., the Cardan angle sequence) around the angles used as state. If angles are used as state there is the slight disadvantage that a singular configuration is present leading to a division by zero.

  • isroot: Indicate that frame_a is the root, otherwise frame_b is the root. Only relevant if state = true.
  • sequence: Rotation sequence
  • d: Viscous damping constant. If d > 0. the joint dissipates energy due to viscous damping according to $τ ~ -d*ω$.

Rendering options

  • radius = 0.1: Radius of the joint in animations
  • color = [1,1,0,1]: Color of the joint in animations, a vector of length 4 with values between [0, 1] providing RGBA values
  • render = true: Render the joint in animations
source
Multibody.SphericalConstraintMethod
SphericalConstraint(; name, color = [1, 1, 0, 1], radius = 0.1, x_locked = true, y_locked = true, z_locked = true)

Spherical cut joint and translational directions may be constrained or released

This model does not use explicit variables e.g. state variables in order to describe the relative motion of frame_b with to respect to frame_a, but defines kinematic constraints between the frame_a and frame_b. The forces and torques at both frames are then evaluated in such a way that the constraints are satisfied. Sometimes this type of formulation is also called an implicit joint in literature.

As a consequence of the formulation the relative kinematics between frame_a and frame_b cannot be initialized.

In complex multibody systems with closed loops this may help to simplify the system of non-linear equations. Please compare state realization chosen by structural_simplify using the classical joint formulation and the alternative formulation used here in order to check whether this fact applies to the particular system under consideration. In systems without closed loops the use of this implicit joint is not recommended.

Arguments

  • x_locked: Set to false if the translational motion in x-direction shall be free
  • y_locked: Set to false if the translational motion in y-direction shall be free
  • z_locked: Set to false if the translational motion in z-direction shall be free

Rendering parameters

  • color: Color of the joint in animations (RGBA)
  • radius: Radius of the joint in animations
source
Multibody.SphericalSphericalMethod
SphericalSpherical(; name, state = false, isroot = true, iscut=false, w_rel_a_fixed = false, r_0 = [0,0,0], color = [1, 1, 0, 1], m = 0, radius = 0.1, kinematic_constraint=true)

Joint that has a spherical joint on each of its two ends. The rod connecting the two spherical joints is approximated by a point mass that is located in the middle of the rod. When the mass is set to zero (default), special code for a massless body is generated.

This joint introduces one constraint defining that the distance between the origin of frame_a and the origin of frame_b is constant (= rodLength). It is highly recommended to use this joint in loops whenever possible, because this enhances the efficiency considerably due to smaller systems of non-linear algebraic equations.

It is not possible to connect other components, such as a body with mass properties or a special visual shape object to the rod connecting the two spherical joints. If this is needed, use instead joint UniversalSpherical that has the additional frame frame_ia for this.

Connectors:

  • frame_a: Frame for the first spherical joint
  • frame_b: Frame for the second spherical joint

Rendering parameters:

  • radius: Radius of the joint in animations
  • color: Color of the joint in animations (RGBA)
source
Multibody.UniversalMethod
Universal(; name, n_a, n_b, phi_a = 0, phi_b = 0, w_a = 0, w_b = 0, a_a = 0, a_b = 0, state_priority=10)

Joint where frame_a rotates around axis n_a which is fixed in frame_a and frame_b rotates around axis n_b which is fixed in frame_b. The two frames coincide when revolute_a.phi=0 and revolute_b.phi=0. This joint has the following potential states;

  • The relative angle phi_a = revolute_a.phi [rad] around axis n_a
  • the relative angle phi_b = revolute_b.phi [rad] around axis n_b
  • the relative angular velocity w_a = D(phi_a)
  • the relative angular velocity w_b = D(phi_b)
source
Multibody.UniversalSphericalMethod
UniversalSpherical(; name, n1_a, rRod_ia, sphere_diameter = 0.1, sphere_color, rod_width = 0.1, rod_height = 0.1, rod_color, cylinder_length = 0.1, cylinder_diameter = 0.1, cylinder_color, kinematic_constraint = true)

Universal - spherical joint aggregation (1 constraint, no potential states)

This component consists of a universal joint at frame_a and a spherical joint at frame_b that are connected together with a rigid rod.

This joint aggregation has no mass and no inertia and introduces the constraint that the distance between the origin of frame_a and the origin of frame_b is constant (= length(rRod_ia)). The universal joint is defined in the following way:

  • The rotation axis of revolute joint 1 is along parameter vector n1_a which is fixed in frame_a.
  • The rotation axis of revolute joint 2 is perpendicular to axis 1 and to the line connecting the universal and the spherical joint.

Note, there is a singularity when axis 1 and the connecting rod are parallel to each other. Therefore, if possible n1_a should be selected in such a way that it is perpendicular to rRod_ia in the initial configuration (i.e., the distance to the singularity is as large as possible).

An additional frame_ia is present. It is fixed in the connecting rod at the origin of frame_a. The placement of frame_ia on the rod is implicitly defined by the universal joint (frame_a and frame_ia coincide when the angles of the two revolute joints of the universal joint are zero) and by parameter vector rRod_ia, the position vector from the origin of frame_a to the origin of frame_b, resolved in frame_ia.

This joint aggregation can be used in cases where in reality a rod with spherical joints at end are present. Such a system has an additional degree of freedom to rotate the rod along its axis. In practice this rotation is usually of no interest and is mathematically removed by replacing one of the spherical joints by a universal joint. Still, in most cases the SphericalSpherical joint aggregation can be used instead of the UniversalSpherical joint since the rod is animated and its mass properties are approximated by a point mass in the middle of the rod. The SphericalSpherical joint has the advantage that it does not have a singular configuration.

Arguments

  • n1_a Axis 1 of universal joint resolved in frame_a (axis 2 is orthogonal to axis 1 and to rod)
  • rRod_ia Vector from origin of framea to origin of frameb, resolved in frame_ia (if computeRodLength=true, rRod_ia is only an axis vector along the connecting rod)
  • kinematic_constraint = true Set to false if no constraint shall be defined, due to analytically solving a kinematic loop
  • constraint_residue If set to :external, an equation in the parent system is expected to define this variable, e.g., rod.constraint_residue ~ rod.f_rod - f_rod where rod is the name of the UniversalSpherical joint. If unspecified, the length constraint rRod_0'rRod_0 - rodLength'rodLength is used

Connectors

  • frame_a: Frame for the universal joint
  • frame_b: Frame for the spherical joint
  • frame_ia: Frame fixed in the rod at the origin of frame_a

Rendering parameters

  • sphere_diameter: Diameter of spheres representing the universal and the spherical joint
  • sphere_color: Color of spheres representing the universal and the spherical joint (RGBA)
  • rod_width: Width of rod shape in direction of axis 2 of universal joint
  • rod_height: Height of rod shape in direction that is orthogonal to rod and to axis 2
  • rod_color: Color of rod shape connecting the universal and the spherical joints (RGBA)
  • cylinder_length: Length of cylinders representing the two universal joint axes
  • cylinder_diameter: Diameter of cylinders representing the two universal joint axes
  • cylinder_color: Color of cylinders representing the two universal joint axes (RGBA)
source
Multibody.PlanarMechanics.PrismaticMethod
Prismatic(; name, f, s = 0, axisflange = false)

A prismatic joint

Parameters

  • r: [m, m] x,y-direction of the rod wrt. body system at phi=0
  • axisflange=false: If true, a force flange is enabled, otherwise implicitly grounded"
  • render: Render the joint in animations
  • radius: Radius of the body in animations
  • color: Color of the body in animations

Variables

  • s(t): [m] Elongation of the joint
  • v(t): [m/s] Velocity of elongation
  • a(t): [m/s²] Acceleration of elongation
  • f(t): [N] Force in direction of elongation

Connectors

source
Multibody.PlanarMechanics.RevoluteMethod
Revolute(; name, phi = 0.0, tau = 0.0, axisflange = false)

A revolute joint

Parameters:

  • axisflange=false: If true, a force flange is enabled, otherwise implicitly grounded"
  • phi: [rad] Initial angular position for the flange
  • tau: [Nm] Initial Cut torque in the flange

Variables:

  • phi(t): [rad] angular position
  • w(t): [rad/s] angular velocity
  • α(t): [rad/s²] angular acceleration
  • tau(t): [Nm] torque

Connectors

source
+)

This component consists of a universal joint at frame_a, a revolute joint at frame_b and a spherical joint which is connected via rod1 to the universal and via rod2 to the revolute joint.

This joint aggregation has no mass and no inertia and introduces neither constraints nor potential state variables. It should be used in kinematic loops whenever possible since the non-linear system of equations introduced by this joint aggregation is solved analytically (i.e., a solution is always computed, if a unique solution exists).

The universal joint is defined in the following way:

  • The rotation axis of revolute joint 1 is along parameter vector n1_a which is fixed in frame_a.
  • The rotation axis of revolute joint 2 is perpendicular to axis 1 and to the line connecting the universal and the spherical joint (= rod 1).

The definition of axis 2 of the universal joint is performed according to the most often occurring case for the sake of simplicity. Otherwise, the treatment is much more complicated and the number of operations is considerably higher, if axis 2 is not orthogonal to axis 1 and to the connecting rod.

Note, there is a singularity when axis 1 and the connecting rod are parallel to each other. Therefore, if possible n1_a should be selected in such a way that it is perpendicular to rRod1_ia in the initial configuration (i.e., the distance to the singularity is as large as possible).

The rest of this joint aggregation is defined by the following parameters:

  • positive_branch: The positive branch of the revolute joint is selected (cf. elbow up vs. elbow down).
  • The position of the spherical joint with respect to the universal joint is defined by vector rRod1_ia. This vector is directed from frame_a to the spherical joint and is resolved in frame_ia (it is most simple to select frame_ia such that it is parallel to frame_a in the reference or initial configuration).
  • The position of the spherical joint with respect to the revolute joint is defined by vector rRod2_ib. This vector is directed from the inner frame of the revolute joint (frame_ib or revolute.frame_a) to the spherical joint and is resolved in frame_ib (note, that frame_ib and frame_b are parallel to each other).
  • The axis of rotation of the revolute joint is defined by axis vector n_b. It is fixed and resolved in frame_b.
  • When specifying this joint aggregation with the definitions above, two different configurations are possible. Via parameter phi_guess a guess value for revolute.phi(t0) at the initial time t0 is given. The configuration is selected that is closest to phi_guess (|revolute.phi - phi_guess| is minimal).

Connectors

  • frame_a: Frame for the universal joint
  • frame_b: Frame for the revolute joint
  • An additional frame_ia is present. It is fixed in the rod connecting the universal and the spherical joint at the origin of frame_a. The placement of frame_ia on the rod is implicitly defined by the universal joint (frame_a and frame_ia coincide when the angles of the two revolute joints of the universal joint are zero) and by parameter vector rRod1ia, the position vector from the origin of `frameato the spherical joint, resolved inframe_ia`.
  • An additional frame_ib is present. It is fixed in the rod connecting the revolute and the spherical joint at the side of the revolute joint that is connected to this rod (= rod2.frame_a = revolute.frame_a).
  • An additional frame_im is present. It is fixed in the rod connecting the revolute and the spherical joint at the side of the spherical joint that is connected to this rod (= rod2.frame_b). It is always parallel to frame_ib.
source
Multibody.PrismaticMethod
Prismatic(; name, n = [0, 0, 1], axisflange = false)

Prismatic joint with 1 translational degree-of-freedom

  • n: The axis of motion (unit vector)
  • axisflange: If true, the joint will have two additional frames from Mechanical.Translational, axis and support, between which translational components such as springs and dampers can be connected.

If axisflange, flange connectors for ModelicaStandardLibrary.Mechanics.TranslationalModelica are also available:

  • axis: 1-dim. translational flange that drives the joint
  • support: 1-dim. translational flange of the drive support (assumed to be fixed in the world frame, NOT in the joint)

The function returns an ODESystem representing the prismatic joint.

source
Multibody.PrismaticConstraintMethod
PrismaticConstraint(; name, color, radius = 0.05, x_locked = true, y_locked = true, z_locked = true, render = true)

This model does not use explicit variables e.g. state variables in order to describe the relative motion of frame_b with respect to frame_a, but defines kinematic constraints between the frame_a and frame_b. The forces and torques at both frames are then evaluated in such a way that the constraints are satisfied. Sometimes this type of formulation is called an implicit joint in literature.

As a consequence of the formulation, the relative kinematics between frame_a and frame_b cannot be initialized.

In complex multibody systems with closed loops this may help to simplify the system of non-linear equations. Compare the simplification result using the classical joint formulation and this alternative formulation to check which one is more efficient for the particular system under consideration.

In systems without closed loops the use of this implicit joint does not make sense or may even be disadvantageous.

Parameters

  • color: Color of the joint in animations (RGBA)
  • radius: Radius of the joint in animations
  • x_locked: Set to false if the translational motion in x-direction shall be free
  • y_locked: Set to false if the translational motion in y-direction shall be free
  • z_locked: Set to false if the translational motion in z-direction shall be free
  • render: Whether or not the joint is rendered in animations
source
Multibody.RevoluteMethod
Revolute(; name, phi0 = 0, w0 = 0, n, axisflange = false)

Revolute joint with 1 rotational degree-of-freedom

  • phi0: Initial angle
  • w0: Iniitial angular velocity
  • n: The axis of rotation
  • axisflange: If true, the joint will have two additional frames from Mechanical.Rotational, axis and support, between which rotational components such as springs and dampers can be connected.

If axisflange, flange connectors for ModelicaStandardLibrary.Mechanics.Rotational are also available:

  • axis: 1-dim. rotational flange that drives the joint
  • support: 1-dim. rotational flange of the drive support (assumed to be fixed in the world frame, NOT in the joint)

Rendering options

  • radius = 0.05: Radius of the joint in animations
  • length = radius: Length of the joint in animations
  • color: Color of the joint in animations, a vector of length 4 with values between [0, 1] providing RGBA values
source
Multibody.RevolutePlanarLoopConstraintMethod
RevolutePlanarLoopConstraint(; name, n)

Revolute joint that is described by 2 positional constraints for usage in a planar loop (the ambiguous cut-force perpendicular to the loop and the ambiguous cut-torques are set arbitrarily to zero)

Joint where frame_b rotates around axis n which is fixed in frame_a and where this joint is used in a planar loop providing 2 constraint equations on position level.

If a planar loop is present, e.g., consisting of 4 revolute joints where the joint axes are all parallel to each other, then there is no unique mathematical solution if all revolute joints are modelled with Revolute and the symbolic algorithms will fail. The reason is that, e.g., the cut-forces in the revolute joints perpendicular to the planar loop are not uniquely defined when 3-dim. descriptions of revolute joints are used. In this case, one revolute joint in the loop has to be replaced by model RevolutePlanarLoopConstraint. The effect is that from the 5 constraints of a 3-dim. revolute joint, 3 constraints are removed and replaced by appropriate known variables (e.g., the force in the direction of the axis of rotation is treated as known with value equal to zero; for standard revolute joints, this force is an unknown quantity).

source
Multibody.SphericalMethod
Spherical(; name, state = false, isroot = true, w_rel_a_fixed = false, z_rel_a_fixed = false, sequence, phi = 0, phid = 0, phidd = 0, d = 0)

Joint with 3 constraints that define that the origin of frame_a and the origin of frame_b coincide. By default this joint defines only the 3 constraints without any potential state variables. If parameter state is set to true, three states are introduced. The orientation of frame_b is computed by rotating frame_a along the axes defined in parameter vector sequence (default = [1,2,3], i.e., the Cardan angle sequence) around the angles used as state. If angles are used as state there is the slight disadvantage that a singular configuration is present leading to a division by zero.

  • isroot: Indicate that frame_a is the root, otherwise frame_b is the root. Only relevant if state = true.
  • sequence: Rotation sequence
  • d: Viscous damping constant. If d > 0. the joint dissipates energy due to viscous damping according to $τ ~ -d*ω$.

Rendering options

  • radius = 0.1: Radius of the joint in animations
  • color = [1,1,0,1]: Color of the joint in animations, a vector of length 4 with values between [0, 1] providing RGBA values
  • render = true: Render the joint in animations
source
Multibody.SphericalConstraintMethod
SphericalConstraint(; name, color = [1, 1, 0, 1], radius = 0.1, x_locked = true, y_locked = true, z_locked = true)

Spherical cut joint and translational directions may be constrained or released

This model does not use explicit variables e.g. state variables in order to describe the relative motion of frame_b with to respect to frame_a, but defines kinematic constraints between the frame_a and frame_b. The forces and torques at both frames are then evaluated in such a way that the constraints are satisfied. Sometimes this type of formulation is also called an implicit joint in literature.

As a consequence of the formulation the relative kinematics between frame_a and frame_b cannot be initialized.

In complex multibody systems with closed loops this may help to simplify the system of non-linear equations. Please compare state realization chosen by structural_simplify using the classical joint formulation and the alternative formulation used here in order to check whether this fact applies to the particular system under consideration. In systems without closed loops the use of this implicit joint is not recommended.

Arguments

  • x_locked: Set to false if the translational motion in x-direction shall be free
  • y_locked: Set to false if the translational motion in y-direction shall be free
  • z_locked: Set to false if the translational motion in z-direction shall be free

Rendering parameters

  • color: Color of the joint in animations (RGBA)
  • radius: Radius of the joint in animations
source
Multibody.SphericalSphericalMethod
SphericalSpherical(; name, state = false, isroot = true, iscut=false, w_rel_a_fixed = false, r_0 = [0,0,0], color = [1, 1, 0, 1], m = 0, radius = 0.1, kinematic_constraint=true)

Joint that has a spherical joint on each of its two ends. The rod connecting the two spherical joints is approximated by a point mass that is located in the middle of the rod. When the mass is set to zero (default), special code for a massless body is generated.

This joint introduces one constraint defining that the distance between the origin of frame_a and the origin of frame_b is constant (= rodLength). It is highly recommended to use this joint in loops whenever possible, because this enhances the efficiency considerably due to smaller systems of non-linear algebraic equations.

It is not possible to connect other components, such as a body with mass properties or a special visual shape object to the rod connecting the two spherical joints. If this is needed, use instead joint UniversalSpherical that has the additional frame frame_ia for this.

Connectors:

  • frame_a: Frame for the first spherical joint
  • frame_b: Frame for the second spherical joint

Rendering parameters:

  • radius: Radius of the joint in animations
  • color: Color of the joint in animations (RGBA)
source
Multibody.UniversalMethod
Universal(; name, n_a, n_b, phi_a = 0, phi_b = 0, w_a = 0, w_b = 0, a_a = 0, a_b = 0, state_priority=10)

Joint where frame_a rotates around axis n_a which is fixed in frame_a and frame_b rotates around axis n_b which is fixed in frame_b. The two frames coincide when revolute_a.phi=0 and revolute_b.phi=0. This joint has the following potential states;

  • The relative angle phi_a = revolute_a.phi [rad] around axis n_a
  • the relative angle phi_b = revolute_b.phi [rad] around axis n_b
  • the relative angular velocity w_a = D(phi_a)
  • the relative angular velocity w_b = D(phi_b)
source
Multibody.UniversalSphericalMethod
UniversalSpherical(; name, n1_a, rRod_ia, sphere_diameter = 0.1, sphere_color, rod_width = 0.1, rod_height = 0.1, rod_color, cylinder_length = 0.1, cylinder_diameter = 0.1, cylinder_color, kinematic_constraint = true)

Universal - spherical joint aggregation (1 constraint, no potential states)

This component consists of a universal joint at frame_a and a spherical joint at frame_b that are connected together with a rigid rod.

This joint aggregation has no mass and no inertia and introduces the constraint that the distance between the origin of frame_a and the origin of frame_b is constant (= length(rRod_ia)). The universal joint is defined in the following way:

  • The rotation axis of revolute joint 1 is along parameter vector n1_a which is fixed in frame_a.
  • The rotation axis of revolute joint 2 is perpendicular to axis 1 and to the line connecting the universal and the spherical joint.

Note, there is a singularity when axis 1 and the connecting rod are parallel to each other. Therefore, if possible n1_a should be selected in such a way that it is perpendicular to rRod_ia in the initial configuration (i.e., the distance to the singularity is as large as possible).

An additional frame_ia is present. It is fixed in the connecting rod at the origin of frame_a. The placement of frame_ia on the rod is implicitly defined by the universal joint (frame_a and frame_ia coincide when the angles of the two revolute joints of the universal joint are zero) and by parameter vector rRod_ia, the position vector from the origin of frame_a to the origin of frame_b, resolved in frame_ia.

This joint aggregation can be used in cases where in reality a rod with spherical joints at end are present. Such a system has an additional degree of freedom to rotate the rod along its axis. In practice this rotation is usually of no interest and is mathematically removed by replacing one of the spherical joints by a universal joint. Still, in most cases the SphericalSpherical joint aggregation can be used instead of the UniversalSpherical joint since the rod is animated and its mass properties are approximated by a point mass in the middle of the rod. The SphericalSpherical joint has the advantage that it does not have a singular configuration.

Arguments

  • n1_a Axis 1 of universal joint resolved in frame_a (axis 2 is orthogonal to axis 1 and to rod)
  • rRod_ia Vector from origin of framea to origin of frameb, resolved in frame_ia (if computeRodLength=true, rRod_ia is only an axis vector along the connecting rod)
  • kinematic_constraint = true Set to false if no constraint shall be defined, due to analytically solving a kinematic loop
  • constraint_residue If set to :external, an equation in the parent system is expected to define this variable, e.g., rod.constraint_residue ~ rod.f_rod - f_rod where rod is the name of the UniversalSpherical joint. If unspecified, the length constraint rRod_0'rRod_0 - rodLength'rodLength is used

Connectors

  • frame_a: Frame for the universal joint
  • frame_b: Frame for the spherical joint
  • frame_ia: Frame fixed in the rod at the origin of frame_a

Rendering parameters

  • sphere_diameter: Diameter of spheres representing the universal and the spherical joint
  • sphere_color: Color of spheres representing the universal and the spherical joint (RGBA)
  • rod_width: Width of rod shape in direction of axis 2 of universal joint
  • rod_height: Height of rod shape in direction that is orthogonal to rod and to axis 2
  • rod_color: Color of rod shape connecting the universal and the spherical joints (RGBA)
  • cylinder_length: Length of cylinders representing the two universal joint axes
  • cylinder_diameter: Diameter of cylinders representing the two universal joint axes
  • cylinder_color: Color of cylinders representing the two universal joint axes (RGBA)
source
Multibody.PlanarMechanics.PrismaticMethod
Prismatic(; name, f, s = 0, axisflange = false)

A prismatic joint

Parameters

  • r: [m, m] x,y-direction of the rod wrt. body system at phi=0
  • axisflange=false: If true, a force flange is enabled, otherwise implicitly grounded"
  • render: Render the joint in animations
  • radius: Radius of the body in animations
  • color: Color of the body in animations

Variables

  • s(t): [m] Elongation of the joint
  • v(t): [m/s] Velocity of elongation
  • a(t): [m/s²] Acceleration of elongation
  • f(t): [N] Force in direction of elongation

Connectors

source
Multibody.PlanarMechanics.RevoluteMethod
Revolute(; name, phi = 0.0, tau = 0.0, axisflange = false)

A revolute joint

Parameters:

  • axisflange=false: If true, a force flange is enabled, otherwise implicitly grounded"
  • phi: [rad] Initial angular position for the flange
  • tau: [Nm] Initial Cut torque in the flange

Variables:

  • phi(t): [rad] angular position
  • w(t): [rad/s] angular velocity
  • α(t): [rad/s²] angular acceleration
  • tau(t): [Nm] torque

Connectors

source
diff --git a/dev/rendering/index.html b/dev/rendering/index.html index 51b49e70..fb4fa99e 100644 --- a/dev/rendering/index.html +++ b/dev/rendering/index.html @@ -1,8 +1,8 @@ 3D rendering · Multibody.jl

3D rendering and animations

Multibody.jl has an automatic 3D-rendering feature that draws a mechanism in 3D. This can be used to create animations of the mechanism's motion from a solution trajectory, as well as to create interactive applications where the evolution of time can be controlled by the user.

The functionality requires the user to install and load one of the Makie backend packages, e.g.,

using GLMakie # Preferred

or

using CairoMakie
Backend choice

GLMakie and WGLMakie produce much nicer-looking animations and are also significantly faster than CairoMakie. CairoMakie may be used to produce the graphics in some web environments if constraints imposed by the web environment do not allow any of the GL alternatives. CairoMakie struggles with the Z-order of drawn objects, sometimes making bodies that should have been visible hidden behind bodies that are further back in the scene.

After that, the render function is the main entry point to create 3D renderings. This function has the following methods:

  • render(model, prob::ODEProblem): this method creates an interactive figure corresponding to the mechanisms configuration at the specified initial condition.
  • render(model, solution): this method creates an animation corresponding to the mechanisms evolution in a simulation trajectory.
  • scene, time = render(model, solution, t::Real): this method opens an interactive window with the mechanism in the configuration corresponding to the time t. Display scene to display the interactive window, and change the time by either dragging the slider in the window, or write to the observable time[] = new_time.

Colors

Many components allows the user to select with which color it is rendered. This choice is made by providing a 4-element array with color values in the order (RGBA), where each value is between 0 and 1. The last value is the alpha channel which determines the opacity, i.e., 1 is opaque and 0 is invisible.

Rendering the world frame

The display of the world frame can be turned off by setting world.render => false in the variable map.

Tracing the path of a frame in 3D visualizations

The path that a frame traces out during simulation can be visualized by passing a vector of frames to the render function using the traces keyword, e.g., render(..., traces=[frame1, frame2]). See the Furuta-pendulum demonstration Going 3D for an example of this.

Camera controls

The camera controls are inherited from Makie, see their documentation for more information. Of particular interest may be the keyboard shortcuts x, y, z, by holding one of these keys and dragging the mouse, the camera will rotate around the corresponding axis. Use keyword argument show_axis = true to function render or pass parameter world.render => true to ODEProblem to display plot axes and/or world axes in the plot.

Rendering API

Multibody.renderFunction
scene       = render(model, prob)
 scene, time = render(model, sol, t::Real; framerate = 30, traces = [])
-path        = render(model, sol, timevec = range(sol.t[1], sol.t[end], step = 1 / framerate); framerate = 30, timescale=1, display=false, loop=1)

Create a 3D animation of a multibody system

Arguments:

  • model: The unsimplified multibody model, i.e., this is the model before any call to structural_simplify.
  • prob: If an ODEProblem is passed, a static rendering of the system at the initial condition is generated.
  • sol: If an ODESolution produced by simulating the system using solve is passed, an animation or dynamic rendering of the system is generated.
  • t: If a single number t is provided, the mechanism at this time is rendered and a scene is returned together with the time as an Observable. Modify time[] = new_time to change the rendering.
  • timevec: If a vector of times is provided, an animation is created and the path to the file on disk is returned.
  • framerate: Number of frames per second.
  • timescale: Scaling of the time vector. This argument can be made to speed up animations (timescale < 1) or slow them down (timescale > 1). A value of timescale = 2 will be 2x slower than real time.
  • loop: The animation will be looped this many times. Please note: looping the animation using this argument is only recommended when display = true for camera manipulation purposes. When the camera is not manipulated, looping the animation by other means is recommended to avoid an increase in the file size.
  • filename controls the name and the file type of the resulting animation
  • traces: An optional array of frames to show the trace of.
  • show_axis = false: Whether or not to show the plot axes, including background grid.

Camera control

The following keyword arguments are available to control the camera pose:

  • x = 2
  • y = 0.5
  • z = 2
  • lookat = [0,0,0]: a three-vector of coordinates indicating the point at which the camera looks.
  • up = [0,1,0]: A vector indicating the direction that is up.
  • display: if true, the figure will be displayed during the recording process and time will advance in real-time. This allows the user to manipulate the camera options using the mouse during the recording.

See also loop_render

source
Multibody.render!Function
did_render::Bool = render!(scene, ::typeof(ComponentConstructor), sys, sol, t)

Each component that can be rendered must have a render! method. This method is called by render for each component in the system.

This method is responsible for drawing the component onto the scene the way it's supposed to look at time t in the solution sol. t is an Observable. It's recommended to follow the pattern

thing = @lift begin
+path        = render(model, sol, timevec = range(sol.t[1], sol.t[end], step = 1 / framerate); framerate = 30, timescale=1, display=false, loop=1)

Create a 3D animation of a multibody system

Arguments:

  • model: The unsimplified multibody model, i.e., this is the model before any call to structural_simplify.
  • prob: If an ODEProblem is passed, a static rendering of the system at the initial condition is generated.
  • sol: If an ODESolution produced by simulating the system using solve is passed, an animation or dynamic rendering of the system is generated.
  • t: If a single number t is provided, the mechanism at this time is rendered and a scene is returned together with the time as an Observable. Modify time[] = new_time to change the rendering.
  • timevec: If a vector of times is provided, an animation is created and the path to the file on disk is returned.
  • framerate: Number of frames per second.
  • timescale: Scaling of the time vector. This argument can be made to speed up animations (timescale < 1) or slow them down (timescale > 1). A value of timescale = 2 will be 2x slower than real time.
  • loop: The animation will be looped this many times. Please note: looping the animation using this argument is only recommended when display = true for camera manipulation purposes. When the camera is not manipulated, looping the animation by other means is recommended to avoid an increase in the file size.
  • filename controls the name and the file type of the resulting animation
  • traces: An optional array of frames to show the trace of.
  • show_axis = false: Whether or not to show the plot axes, including background grid.

Camera control

The following keyword arguments are available to control the camera pose:

  • x = 2
  • y = 0.5
  • z = 2
  • lookat = [0,0,0]: a three-vector of coordinates indicating the point at which the camera looks.
  • up = [0,1,0]: A vector indicating the direction that is up.
  • display: if true, the figure will be displayed during the recording process and time will advance in real-time. This allows the user to manipulate the camera options using the mouse during the recording.

See also loop_render

source
Multibody.render!Function
did_render::Bool = render!(scene, ::typeof(ComponentConstructor), sys, sol, t)

Each component that can be rendered must have a render! method. This method is called by render for each component in the system.

This method is responsible for drawing the component onto the scene the way it's supposed to look at time t in the solution sol. t is an Observable. It's recommended to follow the pattern

thing = @lift begin
     acces relevant coordinates from sol at time t
     create a geometric object that can be rendered
 end
-mesh!(scene, thing; style...)

Returns

A boolean indicating whether or not the component performed any rendering. Typically, all custom methods of this function should return true, while the default fallback method is the only one returning false.

source
+mesh!(scene, thing; style...)

Returns

A boolean indicating whether or not the component performed any rendering. Typically, all custom methods of this function should return true, while the default fallback method is the only one returning false.

source diff --git a/dev/rotations/index.html b/dev/rotations/index.html index 48c208f6..9b3b4bde 100644 --- a/dev/rotations/index.html +++ b/dev/rotations/index.html @@ -24,4 +24,4 @@ Evec = params(E)
3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3):
  0.0
  0.0
- 0.0
rotation_axis(R), rotation_angle(R) # Get an axis-angle representation
([1.0, 0.0, 0.0], 0.0)

Conventions for modeling

See Orientations and directions

Docstrings

    Multibody.RotationMatrixType
    RotationMatrix

    A struct representing a 3D orientation as a rotation matrix.

    If ODESystem is called on a RotationMatrix object o, symbolic variables for o.R and o.w are created and the value of o.R is used as the default value for the symbolic R.

    Fields:

    • R::R3: The rotation 3×3 matrix ∈ SO(3)
    • w: The angular velocity vector
    source
    Multibody.NumRotationMatrixMethod
    NumRotationMatrix(; R = collect(1.0 * I(3)), w = zeros(3), name, varw = false)

    Create a new RotationMatrix struct with symbolic elements. R,w determine default values.

    The primary difference between NumRotationMatrix and RotationMatrix is that the NumRotationMatrix constructor is used in the constructor of a Frame in order to introduce the frame variables, whereas RorationMatrix (the struct) only wraps existing variables.

    • varw: If true, w is a variable, otherwise it is derived from the derivative of R as w = get_w(R).

    Never call this function directly from a component constructor, instead call f = Frame(); R = ori(f) and add f to the subsystems.

    source
    Multibody.absolute_rotationMethod
    R2 = absolute_rotation(R1, Rrel)
    • R1: Orientation object to rotate frame 0 into frame 1
    • Rrel: Orientation object to rotate frame 1 into frame 2
    • R2: Orientation object to rotate frame 0 into frame 2
    source
    Multibody.axes_rotationsFunction
    axes_rotations(sequence, angles, der_angles; name = :R_ar)

    Generate a rotation matrix for a rotation around the specified axes (Euler/Cardan angles).

    source
    Multibody.axis_rotationMethod
    axis_rotation(sequence, angle; name = :R)

    Generate a rotation matrix for a rotation around the specified axis.

    • sequence: The axis to rotate around (1: x-axis, 2: y-axis, 3: z-axis)
    • angle: The angle of rotation (in radians)

    Returns a RotationMatrix object.

    source
    Multibody.connect_orientationMethod
    connect_orientation(R1,R2; iscut=false)

    Connect two rotation matrices together, optionally introducing a cut joint. A normal connection of two rotation matrices introduces 9 constraints, while a cut connection introduces 3 constraints only. This is useful to open kinematic loops, see Using cut joints (docs page) for an example where this is used.

    source
    Multibody.get_frameMethod
    T_W_F = get_frame(sol, frame, t)

    Extract a 4×4 transformation matrix ∈ SE(3) from a solution at time t.

    The transformation matrix returned, $T_W^F$, is such that when a homogenous-coordinate vector $p_F$, expressed in the local frame of reference $F$ is multiplied by $T_W^F$ as $Tp$, the resulting vector is $p_W$ expressed in the world frame:

    \[p_W = T_W^F p_F\]

    See also get_trans and get_rot, Orientations and directions (docs section).

    source
    Multibody.get_rotMethod
    R_W_F = get_rot(sol, frame, t)

    Extract a 3×3 rotation matrix ∈ SO(3) from a solution at time t.

    The rotation matrix returned, $R_W^F$, is such that when a vector $p_F$ expressed in the local frame of reference $F$ is multiplied by $R_W^F$ as $Rp$, the resulting vector is $p_W$ expressed in the world frame:

    \[p_W = R_W^F p_F\]

    This is the inverse (transpose) of the rotation matrix stored in frame connectors (e.g. ori(frame).R = get_rot(sol, frame, t)').

    The columns of $R_W_F$ indicate are the basis vectors of the frame $F$ expressed in the world coordinate frame.

    See also get_trans, get_frame, Orientations and directions (docs section).

    source
    Multibody.get_wMethod
    get_w(R)

    Compute the angular velocity w from the rotation matrix R and its derivative DR = D.(R).

    source
    Multibody.planar_rotationMethod
    planar_rotation(axis, phi, phid)

    Generate a rotation matrix for a rotation around the specified axis (axis-angle representation).

    source
    Multibody.resolve1Method
    h1 = resolve1(R21, h2)

    R12 is a 3x3 matrix that transforms a vector from frame 1 to frame 2. h2 is a vector resolved in frame 2. h1 is the same vector in frame 1.

    Typical usage (local to world):

    r_wb = resolve1(ori(frame_a), r_ab)
    source
    Multibody.resolve2Method
    h2 = resolve2(R21, h1)

    R21 is a 3x3 matrix that transforms a vector from frame 1 to frame 2. h1 is a vector resolved in frame 1. h2 is the same vector in frame 2.

    Typical usage (world to local):

    g_a = resolve2(ori(frame_a), a_0 - g_0)
    source
    • quat"Integrating Rotations using Non-Unit Quaternions", Caleb Rucker, https://par.nsf.gov/servlets/purl/10097724
    + 0.0
    rotation_axis(R), rotation_angle(R) # Get an axis-angle representation
    ([1.0, 0.0, 0.0], 0.0)

    Conventions for modeling

    See Orientations and directions

    Docstrings

      Multibody.RotationMatrixType
      RotationMatrix

      A struct representing a 3D orientation as a rotation matrix.

      If ODESystem is called on a RotationMatrix object o, symbolic variables for o.R and o.w are created and the value of o.R is used as the default value for the symbolic R.

      Fields:

      • R::R3: The rotation 3×3 matrix ∈ SO(3)
      • w: The angular velocity vector
      source
      Multibody.NumRotationMatrixMethod
      NumRotationMatrix(; R = collect(1.0 * I(3)), w = zeros(3), name, varw = false)

      Create a new RotationMatrix struct with symbolic elements. R,w determine default values.

      The primary difference between NumRotationMatrix and RotationMatrix is that the NumRotationMatrix constructor is used in the constructor of a Frame in order to introduce the frame variables, whereas RorationMatrix (the struct) only wraps existing variables.

      • varw: If true, w is a variable, otherwise it is derived from the derivative of R as w = get_w(R).

      Never call this function directly from a component constructor, instead call f = Frame(); R = ori(f) and add f to the subsystems.

      source
      Multibody.absolute_rotationMethod
      R2 = absolute_rotation(R1, Rrel)
      • R1: Orientation object to rotate frame 0 into frame 1
      • Rrel: Orientation object to rotate frame 1 into frame 2
      • R2: Orientation object to rotate frame 0 into frame 2
      source
      Multibody.axes_rotationsFunction
      axes_rotations(sequence, angles, der_angles; name = :R_ar)

      Generate a rotation matrix for a rotation around the specified axes (Euler/Cardan angles).

      source
      Multibody.axis_rotationMethod
      axis_rotation(sequence, angle; name = :R)

      Generate a rotation matrix for a rotation around the specified axis.

      • sequence: The axis to rotate around (1: x-axis, 2: y-axis, 3: z-axis)
      • angle: The angle of rotation (in radians)

      Returns a RotationMatrix object.

      source
      Multibody.connect_orientationMethod
      connect_orientation(R1,R2; iscut=false)

      Connect two rotation matrices together, optionally introducing a cut joint. A normal connection of two rotation matrices introduces 9 constraints, while a cut connection introduces 3 constraints only. This is useful to open kinematic loops, see Using cut joints (docs page) for an example where this is used.

      source
      Multibody.get_frameMethod
      T_W_F = get_frame(sol, frame, t)

      Extract a 4×4 transformation matrix ∈ SE(3) from a solution at time t.

      The transformation matrix returned, $T_W^F$, is such that when a homogenous-coordinate vector $p_F$, expressed in the local frame of reference $F$ is multiplied by $T_W^F$ as $Tp$, the resulting vector is $p_W$ expressed in the world frame:

      \[p_W = T_W^F p_F\]

      See also get_trans and get_rot, Orientations and directions (docs section).

      source
      Multibody.get_rotMethod
      R_W_F = get_rot(sol, frame, t)

      Extract a 3×3 rotation matrix ∈ SO(3) from a solution at time t.

      The rotation matrix returned, $R_W^F$, is such that when a vector $p_F$ expressed in the local frame of reference $F$ is multiplied by $R_W^F$ as $Rp$, the resulting vector is $p_W$ expressed in the world frame:

      \[p_W = R_W^F p_F\]

      This is the inverse (transpose) of the rotation matrix stored in frame connectors (e.g. ori(frame).R = get_rot(sol, frame, t)').

      The columns of $R_W_F$ indicate are the basis vectors of the frame $F$ expressed in the world coordinate frame.

      See also get_trans, get_frame, Orientations and directions (docs section).

      source
      Multibody.get_wMethod
      get_w(R)

      Compute the angular velocity w from the rotation matrix R and its derivative DR = D.(R).

      source
      Multibody.planar_rotationMethod
      planar_rotation(axis, phi, phid)

      Generate a rotation matrix for a rotation around the specified axis (axis-angle representation).

      source
      Multibody.resolve1Method
      h1 = resolve1(R21, h2)

      R12 is a 3x3 matrix that transforms a vector from frame 1 to frame 2. h2 is a vector resolved in frame 2. h1 is the same vector in frame 1.

      Typical usage (local to world):

      r_wb = resolve1(ori(frame_a), r_ab)
      source
      Multibody.resolve2Method
      h2 = resolve2(R21, h1)

      R21 is a 3x3 matrix that transforms a vector from frame 1 to frame 2. h1 is a vector resolved in frame 1. h2 is the same vector in frame 2.

      Typical usage (world to local):

      g_a = resolve2(ori(frame_a), a_0 - g_0)
      source
      • quat"Integrating Rotations using Non-Unit Quaternions", Caleb Rucker, https://par.nsf.gov/servlets/purl/10097724
      diff --git a/dev/sensors/index.html b/dev/sensors/index.html index 638d7a2b..27766408 100644 --- a/dev/sensors/index.html +++ b/dev/sensors/index.html @@ -1,6 +1,6 @@ -Sensors · Multibody.jl

      Sensors

      A sensor is an object that translates quantities in the mechanical domain into causal signals which can interact with causal components from ModelingToolkitStandardLibrary.Blocks, such as control systems etc.

      Docstrings

      Multibody.CutForceMethod
      BasicCutForce(; name, resolve_frame)

      Basic sensor to measure cut force vector. Contains a connector of type Blocks.RealOutput with name force.

      • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_a, options include :frame_a and :world.
      source
      Multibody.CutTorqueMethod
      CutTorque(; name, resolve_frame)

      Basic sensor to measure cut torque vector. Contains a connector of type Blocks.RealOutput with name torque.

      • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_a, options include :frame_a and :world.
      source
      Multibody.PartialCutForceBaseSensorMethod
      PartialCutForceBaseSensor(; name, resolve_frame = :frame_a)
      • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_a, options include :frame_a and :world.
      source
      Multibody.PowerMethod
      Power(; name)

      A sensor measuring mechanical power transmitted from frame_a to frame_b.

      Connectors:

      power of type RealOutput.

      source
      Multibody.PlanarMechanics.PartialAbsoluteBaseSensorConstant
      PartialAbsoluteBaseSensor(;name)

      Partial absolute sensor models for sensors defined by equations (frame_resolve must be connected exactly once)

      Connectors:

      • frame_a: 2-dim. Coordinate system from which kinematic quantities are measured
      • frame_resolve: 2-dim. Coordinate system in which vector is optionally resolved
      source
      Multibody.PlanarMechanics.AbsolutePositionMethod
      AbsolutePosition(;name, resolve_in_frame = :frame_a)

      Measure absolute position and orientation of the origin of frame connector

      Connectors:

      • x: [m] x-position
      • y: [m] y-position
      • phi: [rad] rotation angle (counterclockwise)

      Parameters:

      • resolve_in_frame: Frame in which output x, y, phi is resolved (1: :world, 2: :framea, 3: :frameresolve)
      source
      Multibody.PlanarMechanics.BasicAbsolutePositionMethod
      BasicAbsolutePosition(;name, resolve_in_frame = :frame_a)

      Measure absolute position and orientation (same as Sensors.AbsolutePosition, but frame_resolve is not conditional and must be connected).

      Connectors:

      • x: [m] x-position
      • y: [m] y-position
      • phi: [rad] rotation angle (counterclockwise)
      • frame_a: Coordinate system a
      • frame_resolve: 2-dim. Coordinate system in which vector is optionally resolved

      Parameters:

      • resolve_in_frame: Frame in which output x, y, phi r is resolved (1: :world, 2: :framea, 3: :frameresolve)
      source
      Multibody.PlanarMechanics.BasicRelativePositionMethod
      BasicRelativePosition(; name, resolve_in_frame = :frame_a)

      Measure relative position and orientation between the origins of two frame connectors

      Connectors:

      • rel_x: [m] Relative x-position
      • rel_y: [m] Relative y-position
      • rel_phi: [rad] Relative rotation angle (counterclockwise)
      • frame_a: Coordinate system a
      • frame_b: Coordinate system b
      • frame_resolve:

      Parameters:

      - `resolve_in_frame`: Frame in which output x, y, phi is resolved (1: :world, 2: :frame_a, 3: frame_b 4: :frame_resolve)
      source
      Multibody.PlanarMechanics.RelativePositionMethod
      RelativePosition(; name, resolve_in_frame = :frame_a)

      Measure relative position and orientation between the origins of two frame connectors

      Connectors:

      - `rel_x`: [m] Relative x-position
      +Sensors · Multibody.jl

      Sensors

      A sensor is an object that translates quantities in the mechanical domain into causal signals which can interact with causal components from ModelingToolkitStandardLibrary.Blocks, such as control systems etc.

      Docstrings

      Multibody.CutForceMethod
      BasicCutForce(; name, resolve_frame)

      Basic sensor to measure cut force vector. Contains a connector of type Blocks.RealOutput with name force.

      • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_a, options include :frame_a and :world.
      source
      Multibody.CutTorqueMethod
      CutTorque(; name, resolve_frame)

      Basic sensor to measure cut torque vector. Contains a connector of type Blocks.RealOutput with name torque.

      • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_a, options include :frame_a and :world.
      source
      Multibody.PartialCutForceBaseSensorMethod
      PartialCutForceBaseSensor(; name, resolve_frame = :frame_a)
      • resolve_frame: The frame in which the cut force and cut torque are resolved. Default is :frame_a, options include :frame_a and :world.
      source
      Multibody.PowerMethod
      Power(; name)

      A sensor measuring mechanical power transmitted from frame_a to frame_b.

      Connectors:

      power of type RealOutput.

      source
      Multibody.PlanarMechanics.PartialAbsoluteBaseSensorConstant
      PartialAbsoluteBaseSensor(;name)

      Partial absolute sensor models for sensors defined by equations (frame_resolve must be connected exactly once)

      Connectors:

      • frame_a: 2-dim. Coordinate system from which kinematic quantities are measured
      • frame_resolve: 2-dim. Coordinate system in which vector is optionally resolved
      source
      Multibody.PlanarMechanics.AbsolutePositionMethod
      AbsolutePosition(;name, resolve_in_frame = :frame_a)

      Measure absolute position and orientation of the origin of frame connector

      Connectors:

      • x: [m] x-position
      • y: [m] y-position
      • phi: [rad] rotation angle (counterclockwise)

      Parameters:

      • resolve_in_frame: Frame in which output x, y, phi is resolved (1: :world, 2: :framea, 3: :frameresolve)
      source
      Multibody.PlanarMechanics.BasicAbsolutePositionMethod
      BasicAbsolutePosition(;name, resolve_in_frame = :frame_a)

      Measure absolute position and orientation (same as Sensors.AbsolutePosition, but frame_resolve is not conditional and must be connected).

      Connectors:

      • x: [m] x-position
      • y: [m] y-position
      • phi: [rad] rotation angle (counterclockwise)
      • frame_a: Coordinate system a
      • frame_resolve: 2-dim. Coordinate system in which vector is optionally resolved

      Parameters:

      • resolve_in_frame: Frame in which output x, y, phi r is resolved (1: :world, 2: :framea, 3: :frameresolve)
      source
      Multibody.PlanarMechanics.BasicRelativePositionMethod
      BasicRelativePosition(; name, resolve_in_frame = :frame_a)

      Measure relative position and orientation between the origins of two frame connectors

      Connectors:

      • rel_x: [m] Relative x-position
      • rel_y: [m] Relative y-position
      • rel_phi: [rad] Relative rotation angle (counterclockwise)
      • frame_a: Coordinate system a
      • frame_b: Coordinate system b
      • frame_resolve:

      Parameters:

      - `resolve_in_frame`: Frame in which output x, y, phi is resolved (1: :world, 2: :frame_a, 3: frame_b 4: :frame_resolve)
      source
      Multibody.PlanarMechanics.RelativePositionMethod
      RelativePosition(; name, resolve_in_frame = :frame_a)

      Measure relative position and orientation between the origins of two frame connectors

      Connectors:

      - `rel_x`: [m] Relative x-position
       - `re_y`: [m] Relative y-position
       - `rel_phi`: [rad] Relative rotation angle (counterclockwise)
       - `frame_a`: Coordinate system a
      -- `frame_b`: Coordinate system b

      Parameters:

      - `resolve_in_frame`: Frame in which output x, y, phi is resolved (1: :world, 2: :frame_a, 3: frame_b 4: :frame_resolve)
      source
      +- `frame_b`: Coordinate system b

      Parameters:

      - `resolve_in_frame`: Frame in which output x, y, phi is resolved (1: :world, 2: :frame_a, 3: frame_b 4: :frame_resolve)
      source
      diff --git a/dev/trajectory_planning/f393e660.svg b/dev/trajectory_planning/1309f726.svg similarity index 97% rename from dev/trajectory_planning/f393e660.svg rename to dev/trajectory_planning/1309f726.svg index cd695e2b..e5337646 100644 --- a/dev/trajectory_planning/f393e660.svg +++ b/dev/trajectory_planning/1309f726.svg @@ -1,108 +1,108 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/trajectory_planning/919aef15.svg b/dev/trajectory_planning/cb8d3622.svg similarity index 95% rename from dev/trajectory_planning/919aef15.svg rename to dev/trajectory_planning/cb8d3622.svg index ec24ee54..edbdec1d 100644 --- a/dev/trajectory_planning/919aef15.svg +++ b/dev/trajectory_planning/cb8d3622.svg @@ -1,99 +1,99 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/trajectory_planning/index.html b/dev/trajectory_planning/index.html index 75d50443..8bc844b4 100644 --- a/dev/trajectory_planning/index.html +++ b/dev/trajectory_planning/index.html @@ -9,8 +9,8 @@ q, qd, qdd = point_to_point(t; q1, qd_max, qdd_max) plot(t, [q qd qdd], ylabel=["\$q\$" "\$\\dot{q}\$" "\$\\ddot{q}\$"], layout=(3,1), l=2, sp=[1 1 2 2 3 3], legend=false) -hline!([qd_max' qdd_max'], l=(2, :dash), sp=[2 2 3 3], c=[1 2 1 2], legend=false)Example block output

      5:th order polynomial trajectory

      t = 0:Ts:3
      +hline!([qd_max' qdd_max'], l=(2, :dash), sp=[2 2 3 3], c=[1 2 1 2], legend=false)
      Example block output

      5:th order polynomial trajectory

      t = 0:Ts:3
       q1 = 1
       q, qd, qdd = traj5(t; q1)
       
      -plot(t, [q qd qdd], ylabel=["\$q\$" "\$\\dot{q}\$" "\$\\ddot{q}\$"], layout=(3,1), l=2, legend=false)
      Example block output

      Docstrings

      Multibody.Kinematic5Method
      Kinematic5(; time, name, q0 = 0, q1 = 1, qd0 = 0, qd1 = 0, qdd0 = 0, qdd1 = 0)

      A component emitting a 5:th order polynomial trajectory created using traj5. traj5 is a simple trajectory planner that plans a 5:th order polynomial trajectory between two points, subject to specified boundary conditions on the position, velocity and acceleration.

      Arguments

      • time: Time vector, e.g., 0:0.01:10
      • name: Name of the component

      Outputs

      • q: Position
      • qd: Velocity
      • qdd: Acceleration
      source
      Multibody.KinematicPTPMethod
      KinematicPTP(; time, name, q0 = 0, q1 = 1, qd_max=1, qdd_max=1)

      A component emitting a trajectory created by the point_to_point trajectory generator.

      Arguments

      • time: Time vector, e.g., 0:0.01:10
      • name: Name of the component
      • q0: Initial position
      • q1: Final position
      • qd_max: Maximum velocity
      • qdd_max: Maximum acceleration

      Outputs

      • q: Position
      • qd: Velocity
      • qdd: Acceleration

      See also Kinematic5.

      source
      Multibody.traj5Method
      q, qd, qdd = traj5(t; q0, q1, q̇0 = zero(q0), q̇1 = zero(q0), q̈0 = zero(q0), q̈1 = zero(q0))

      Generate a 5:th order polynomial trajectory with specified end points, vels and accs.

      See also point_to_point and Kinematic5.

      source
      Multibody.point_to_pointMethod
      q,qd,qdd,t_end = point_to_point(time; q0 = 0.0, q1 = 1.0, t0 = 0, qd_max = 1, qdd_max = 1)

      Generate a minimum-time point-to-point trajectory with specified start and endpoints, not exceeding specified speed and acceleration limits.

      The trajectory produced by this function will typically exhibit piecewise constant accleration, piecewise linear velocity and piecewise quadratic position curves.

      If a vector of time points is provided, the function returns matrices q,qd,qdd of size (length(time), n_dims). If a scalar time point is provided, the function returns q,qd,qdd as vectors with the specified dimension (same dimension as q0). t_end is the time at which the trajectory will reach the specified end position.

      Arguments:

      • time: A scalar or a vector of time points.
      • q0: Initial coordinate, may be a scalar or a vector.
      • q1: End coordinate
      • t0: Tiem at which the motion starts. If time contains time points before t0, the trajectory will stand still at q0 until time reaches t0.
      • qd_max: Maximum allowed speed.
      • qdd_max: Maximum allowed acceleration.

      See also KinematicPTP and traj5.

      source
      +plot(t, [q qd qdd], ylabel=["\$q\$" "\$\\dot{q}\$" "\$\\ddot{q}\$"], layout=(3,1), l=2, legend=false)Example block output

      Docstrings

      Multibody.Kinematic5Method
      Kinematic5(; time, name, q0 = 0, q1 = 1, qd0 = 0, qd1 = 0, qdd0 = 0, qdd1 = 0)

      A component emitting a 5:th order polynomial trajectory created using traj5. traj5 is a simple trajectory planner that plans a 5:th order polynomial trajectory between two points, subject to specified boundary conditions on the position, velocity and acceleration.

      Arguments

      • time: Time vector, e.g., 0:0.01:10
      • name: Name of the component

      Outputs

      • q: Position
      • qd: Velocity
      • qdd: Acceleration
      source
      Multibody.KinematicPTPMethod
      KinematicPTP(; time, name, q0 = 0, q1 = 1, qd_max=1, qdd_max=1)

      A component emitting a trajectory created by the point_to_point trajectory generator.

      Arguments

      • time: Time vector, e.g., 0:0.01:10
      • name: Name of the component
      • q0: Initial position
      • q1: Final position
      • qd_max: Maximum velocity
      • qdd_max: Maximum acceleration

      Outputs

      • q: Position
      • qd: Velocity
      • qdd: Acceleration

      See also Kinematic5.

      source
      Multibody.traj5Method
      q, qd, qdd = traj5(t; q0, q1, q̇0 = zero(q0), q̇1 = zero(q0), q̈0 = zero(q0), q̈1 = zero(q0))

      Generate a 5:th order polynomial trajectory with specified end points, vels and accs.

      See also point_to_point and Kinematic5.

      source
      Multibody.point_to_pointMethod
      q,qd,qdd,t_end = point_to_point(time; q0 = 0.0, q1 = 1.0, t0 = 0, qd_max = 1, qdd_max = 1)

      Generate a minimum-time point-to-point trajectory with specified start and endpoints, not exceeding specified speed and acceleration limits.

      The trajectory produced by this function will typically exhibit piecewise constant accleration, piecewise linear velocity and piecewise quadratic position curves.

      If a vector of time points is provided, the function returns matrices q,qd,qdd of size (length(time), n_dims). If a scalar time point is provided, the function returns q,qd,qdd as vectors with the specified dimension (same dimension as q0). t_end is the time at which the trajectory will reach the specified end position.

      Arguments:

      • time: A scalar or a vector of time points.
      • q0: Initial coordinate, may be a scalar or a vector.
      • q1: End coordinate
      • t0: Tiem at which the motion starts. If time contains time points before t0, the trajectory will stand still at q0 until time reaches t0.
      • qd_max: Maximum allowed speed.
      • qdd_max: Maximum allowed acceleration.

      See also KinematicPTP and traj5.

      source
      diff --git a/dev/urdf/index.html b/dev/urdf/index.html index 8ca3949b..414fa01e 100644 --- a/dev/urdf/index.html +++ b/dev/urdf/index.html @@ -11,4 +11,4 @@ urdf2multibody(filename; extras=true, out) include(out) # Include model, perform simulation and plotting

      Docstring

      Multibody.urdf2multibodyFunction
      urdf2multibody(filename::AbstractString; extras=false, out=nothing, worldconnection = :rigid)

      Translate a URDF file into a Multibody model. Only available if LightXML.jl, Graphs.jl, MetaGraphs.jl and JuliaFormatter.jl are manually installed and loaded by the user.

      Example usage:

      using Multibody, ModelingToolkit, JuliaSimCompiler, LightXML, Graphs, MetaGraphsNext, JuliaFormatter
      -urdf2multibody(joinpath(dirname(pathof(Multibody)), "..", "test/doublependulum.urdf"), extras=true, out="/tmp/urdf_import.jl")

      Keyword arguments

      • extras=false: If true, the generated code will include package imports, a simulation of the model and a rendering of the model.
      • out=nothing: If provided, the generated code will be written to this file, otherwise the string will only be returned.
      • worldconnection=:rigid: If :rigid, the world frame will be connected to the root link with a rigid connection. If a joint constructor is provided, this component will be instantiated and the root link is connected to the world through this, e.g., worldconnection = FreeMotion, ()->Prismatic(n=[0, 1, 0]) etc.

      render_fixed = false: Whether or not to render "fixed" joints. These joints aren't actually joints (no degrees of freedom), they are translated to FixedTranslation or FixedRotation components.

      source

      Limitations

      The URDF import currently has the following limitations:

      • Sensors are not imported.
      • Transmissions are not imported.
      • friction is not translated, but damping is translated to a 1D Damping component attached using an axisflange.
      • Meshes are not fully supported yet, they will be imported as generic shapes (inertial properties are imported).

      Structure of the translated model

      URDF does not store the transformation implied by links in the link itself, instead, the links store visual and inertial geometry information, while the translation between frames is implied by the origin of the following joint(s). Therefore, we do generally not make use of the r argument to bodies, and let this be arbitrarily set. The transformation between two joints is instead encoded as a r and R arguments to each joint, where joints are wrapped in URDFRevolute and URDFPrismatic components respectively. Internally, these wrapper components are comprised of a transformation, FixedTranslation or FixedRotation, followed by the actual joint. The interface to these special joints are identical to their non-wrapped counterparts, i.e., they have the frame_a and frame_b connectors as expected. Due to this approach, we always connect to the frame_a connector of links/bodies and let frame_b be unused.

      +urdf2multibody(joinpath(dirname(pathof(Multibody)), "..", "test/doublependulum.urdf"), extras=true, out="/tmp/urdf_import.jl")

      Keyword arguments

      • extras=false: If true, the generated code will include package imports, a simulation of the model and a rendering of the model.
      • out=nothing: If provided, the generated code will be written to this file, otherwise the string will only be returned.
      • worldconnection=:rigid: If :rigid, the world frame will be connected to the root link with a rigid connection. If a joint constructor is provided, this component will be instantiated and the root link is connected to the world through this, e.g., worldconnection = FreeMotion, ()->Prismatic(n=[0, 1, 0]) etc.

      render_fixed = false: Whether or not to render "fixed" joints. These joints aren't actually joints (no degrees of freedom), they are translated to FixedTranslation or FixedRotation components.

      source

      Limitations

      The URDF import currently has the following limitations:

      • Sensors are not imported.
      • Transmissions are not imported.
      • friction is not translated, but damping is translated to a 1D Damping component attached using an axisflange.
      • Meshes are not fully supported yet, they will be imported as generic shapes (inertial properties are imported).

      Structure of the translated model

      URDF does not store the transformation implied by links in the link itself, instead, the links store visual and inertial geometry information, while the translation between frames is implied by the origin of the following joint(s). Therefore, we do generally not make use of the r argument to bodies, and let this be arbitrarily set. The transformation between two joints is instead encoded as a r and R arguments to each joint, where joints are wrapped in URDFRevolute and URDFPrismatic components respectively. Internally, these wrapper components are comprised of a transformation, FixedTranslation or FixedRotation, followed by the actual joint. The interface to these special joints are identical to their non-wrapped counterparts, i.e., they have the frame_a and frame_b connectors as expected. Due to this approach, we always connect to the frame_a connector of links/bodies and let frame_b be unused.