Components¶
In this tutorial we list the components of the library, try a damper that works one way, and compare how the springs push. A component acts on a coordinate.
The list¶
A component acts on one coordinate \(y\), and it is one of four kinds:
storage: an energy \(V(y)\) and the force \(f = -\partial V / \partial y\) (springs; gravity on the robot);
dissipation: a force with \(f \cdot \dot y \le 0\) (dampers);
inertance: a mass or inertia \(M\), with kinetic energy \(\tfrac12 \dot y^\top M \dot y\);
source: a force whose power \(f \cdot \dot y\) is counted apart in the energy balance (force sources, gravity compensation; see Energy and passivity).
Each force reaches the robot through its coordinate’s Jacobian: \(\dot y = J v\) and \(\tau = J^\top f\). The motors then receive the torques \(u\) with \(B(q)\,u = \tau\), where \(B\) comes from the robot’s transmission. The conventions give the signs. These are the components of the library:
Component |
Kind |
Force on its coordinate |
|---|---|---|
|
storage |
\(-K y\), with \(K\) a scalar, one value per axis, or a matrix |
|
storage |
\(-F \tanh(k y / F)\) per axis: never more than \(F\) |
|
storage |
\(A\, e^{-\lVert y \rVert^2 / 2\sigma^2}\, y\): pushes away from \(y = 0\) |
|
storage |
\(-k(d)\, y\), \(k\) rising from \(k_\min\) to \(k_\max\) around \(d = d_0\), as a logistic curve that is the sharper the larger \(\alpha\) [1/m] |
|
storage |
\(-K (d / d_0)^n\, y\) |
|
storage |
zero inside \([\text{lower}, \text{upper}]\); outside, \(k\) times the overshoot, inwards |
|
storage |
for \(y = (e, \varphi)\), a spring on \(e\) with the stiffness \(K\,(1 + m \cos(\varphi - \varphi_0))\), and the reaction on the phase \(\varphi\); more arguments ( |
|
storage |
\(k \max(0, -d)\) along \(d\), only in contact (\(d < 0\)) |
|
storage |
\(m_i g\) on each mass of the robot |
|
dissipation |
\(-D \dot y\) |
|
dissipation |
\(-F \tanh(D \dot y / F)\) per axis |
|
dissipation |
\(-D \dot y\) in one direction of motion only |
|
dissipation |
\(-D \dot d\), only in contact |
|
dissipation |
\(-\mu F_n\) against the sliding speed along the surface, \(F_n\) the contact spring’s force |
|
inertance |
a mass \(m\) at a point |
|
inertance |
a mass or inertia \(M\) on any coordinate |
|
inertance |
the inertia of a rigid body about its frame, on a |
|
source |
\(f\), a live parameter, bounded in force and in power on request |
|
source |
\(-m_i g\) on each mass of the robot |
|
source |
\(b\,(\omega r(t) - \dot y)\), the ramp \(r\) rising from 0 to 1 in \(T\) |
The arguments are listed in the order the constructors take them, with the symbols of the
formulas; the keyword names (stiffness, damping, max_force, …) are in the
API reference.
An Inertance on a difference of two coordinates is an inerter: a mass between them, with a force
\(M\,(\ddot y_1 - \ddot y_2)\) that opposes their relative acceleration. A LimitSpring on a slice of
the joints, with a lower and an upper bound per joint, gives all of them soft limits.
For the sigmoid and polynomial springs, \(d\) is the size of each axis of \(y\), or its norm with
element_wise=False. A new component only needs its energy or its force
(Extend the library).
A damper that works one way¶
A DiodeDamper(y, D, sign) damps one direction of motion: the positive rates for sign=1, the
negative ones for sign=-1, and never adds energy. A mass of 1 kg on a spring of 100 N/m, let
go at 0.1 m, swings back and forth. Damped both ways it loses its swing at the same rate on each
side (with 2 N·s/m). Damped on the way up only, with twice that, because it works on half of
each swing, it still swings down almost as far as it started:
import numpy as np
import matplotlib.pyplot as plt
import virtualmodelcontrol as vmc
def swing(damper):
"""The height of a mass on a spring, let go at 0.1 m."""
mass = vmc.Mechanism("mass", model=vmc.models.JointSpace(1, unit="m"))
y = mass.joint(0)
mass.add("inertia", vmc.Inertance(y, 1.0))
mass.add("spring", vmc.LinearSpring(y, 100.0))
mass.add("damper", damper(y))
plant = vmc.sim.ModelPlant(mass, q0=[0.1], max_step=1e-4)
out = []
for _ in range(300):
plant.advance(0.01)
out.append(plant.q[0])
return np.array(out)
swings = {"both ways": swing(lambda y: vmc.LinearDamper(y, 2.0)),
"upwards only": swing(lambda y: vmc.DiodeDamper(y, 4.0))}
Its first swing down reaches -0.100 m.
The springs side by side¶
We put six springs on six independent coordinates of a test mechanism, compile them, and read their forces over a range of deflections.
The tanh spring saturates at its maximum force. The Gaussian spring pushes away only near
\(y = 0\), around an obstacle. The sigmoid and polynomial springs stiffen with distance. The limit
spring stays at zero inside its range. On a 3D coordinate the tanh spring saturates each axis on
its own. To bound the force along one direction, put it on a Projection.