Skip to content

Measures, expressions, splines, drivers

Measures

A measure is a named scalar of the state – a distance, a displacement component, an angle, a velocity – used in expression forces and drivers by its name. They are not result channels themselves; to plot one, write it as a formula channel in the viewer, look at the force that uses it, or make it an output.

measures:
  z_wheel: {type: displacement, body1: wheel, component: z, offset: initial}  # travel from the start
  vz_wheel: {type: velocity, body1: wheel, component: z}
  gap: {type: distance, body1: a, point1: [0, 0, 1], body2: b, point2: [0, 0, 2]}
  steer: {type: angle, body1: knuckle, body2: carrier, orientation: [1, 0, 0, 0]}  # about z of the frame
  lean: {type: direction, body1: frame, vector: [0, 0, 1], component: y}         # vertical in the body frame
type value
displacement / velocity a component of P1 − P2 (V1 − V2), in frame (global or a body)
distance / distance_rate |P1 − P2| and its rate
angle / angular_rate rotation of body1 about the z axis of orientation fixed on body2 (continuous: counts turns)
direction a component of a global vector in a frame (default body1), e.g. the lean of a vehicle
angular_velocity a component of ω1 − ω2 in frame

offset: initial subtracts the initial value; a number subtracts that number.

key value
id id
type (required) displacement | velocity | distance | distance_rate | angle | angular_rate | direction | angular_velocity displacement / velocity: component of P1 - P2 / V1 - V2 in frame; distance: |P1 - P2| and its rate; angle: rotation of body1 about the z axis of orientation fixed on body2 (as torsion_spring) and its rate; direction: component of the global vector in the frame (default body1), e.g. the vertical in a vehicle frame (lean); angular_velocity: component of omega1 - omega2 in frame (global or a body).
body1 (required) string
body2 string A body or 'ground'. Default ground.
point1 [x, y, z] or a point name Point on body1, global, at t = 0. Default its center of mass.
point2 [x, y, z] or a point name Point on body2 or the ground, global, at t = 0. Default its center of mass / the origin.
component x | y | z
vector [x, y, z] direction: a global vector.
frame string displacement / velocity: 'global' (default) or a body whose frame gives the components.
orientation list of 4 or a frame name angle: frame (axis z), global at t = 0.
offset number or list initial: subtract the initial value (displacement, distance, angle); a number: subtract it. Default none.
plane list of 2 angle / angular_rate, with vector: the vectors b, c (on body2 or the ground, global at t = 0) of the plane the angle of vector (on body1) is measured in, atan2(a.c, a.b), instead of orientation (legacy Measure Rotational MeasurePlaneVec1 / 2).
normalize boolean angle / angular_rate with vector and plane: normalize the three vectors (false: as given, as legacy Angle and body-body AngleVel measures). Default True.
form legacy angular_rate: legacy gives the numerator x ydot - y xdot of the rate only (legacy AngleVel measures). Default the exact rate.

Expressions

Force laws, driver motions and formulas share one small language (it is parsed, never executed):

  • numbers, + - * / ^ (or **), unary minus, parentheses;
  • names: t (time), pi, the parameters, the runtime inputs and the measures (both not in formulas), the splines as functions: damper_curve(v);
  • functions: sin cos tan asin acos atan atan2 sinh cosh tanh exp log sqrt abs sign min max;
  • step(x, x0, y0, x1, y1): smooth step from y0 (x ≤ x0) to y1 (x ≥ x1) – a 9th order polynomial; step(x, x0, y0, x1, y1, "cosine") or "hard" (the legacy FreeDyn STEP);
  • if_else(condition, a, b) with < <= > >= == !=; conditions multiply as and: if_else((t > 1) * (t < 2), 1, 0).

Everything is differentiated exactly (CasADi); there are no numerical derivatives.

Splines

Named curves y(x), called like functions in expressions: a damper characteristic, a measured road, a motor map.

splines:
  damper_curve: {x: [-1, -0.2, 0, 0.2, 1], y: [-3000, -900, 0, 600, 1500]}   # [m/s] -> [N]
  road: {file: data/road.txt, columns: [0, 1], header_lines: 1}             # from a table
forces:
  damper: {type: expression_force, body1: wheel, body2: body, point1: [0, 0, 0.3], point2: [0, 0, 0.65],
           force: damper_curve(v_rel)}

type: akima (default: no overshoot, linear beyond the ends) or zcc (the legacy FreeDyn spline).

key value
id id
file string Text table with the data instead of x / y (relative to the model file): columns separated by spaces, as the legacy spline data files.
header_lines integer file: lines to skip. Default 0.
columns list of 2 file: column indices (from 0) of x and y. Default [0, 1].
x list Strictly increasing.
y list
type akima | zcc akima (default): Akima, linear beyond the ends; zcc: zero clamped C2 cubic, quadratic beyond the last point (legacy FreeDyn splines).

Outputs

An output is a named expression of measures, parameters and runtime inputs, written as the result channel <name>.value. It acts on nothing. A program that runs the model and reads its channels (fd::System::outputs) gets derived quantities, a lean angle, an engine speed, a suspension travel, without knowing the model's geometry.

measures:
  lean: {type: direction, body1: frame, vector: [0, 0, 1], component: y}   # sin(lean)
  w_crank: {type: angular_rate, body1: crank, body2: frame, orientation: [0.7071068, -0.7071068, 0, 0]}
outputs:
  lean_deg: asin(lean) * 180 / pi
  rpm: w_crank * 30 / pi

Each output must use a measure; its name must differ from the bodies, joints, forces, sensors and drivers.

Drivers

A driver prescribes a measure – a displacement, distance or angle – as a function of time: a constraint m(q) − f(t) = 0. With a runtime input in f the motion is set from outside while the model runs (motion: x_set). The reaction is a result channel (<driver>.lambda).

measures:
  phi: {type: angle, body1: rotor, orientation: [1, 0, 0, 0]}
drivers:
  spin: {measure: phi, motion: w * t}                      # a constant speed
  lift: {measure: z_table, motion: 0.1 * sin(2 * pi * t), ramp: 0.5}   # from rest, smoothly

Without ramp the motion must start where the measure is at t = 0. With ramp: Ta a smooth polynomial joins the initial value at rest to the motion at Ta.

key value
id id
measure (required) string
motion (required) string Expression of t, parameters and splines, e.g. '0.1 * sin(2 * pi * t)'.
ramp number Activation time Ta: for t < Ta a 7th order polynomial from the initial measure value at rest joins the motion C2 at Ta (legacy TimeMotion ActivationPeriod). Default 0: the motion must start at the initial configuration.