Dynamics factors#
A dynamics factor ties consecutive states of a trajectory together through a discrete-time model. Its residual is the multiple-shooting defect of an Euler step over the step duration \(\Delta t_k\),
on the pose (and the difference on vector states), with analytic Jacobians.
Use it as a soft factor (weighted, e.g. for estimation) or as a hard
constraint wrapped in ConstraintFactorBatch(..., ConstraintKind.Equality)
and solved with AugmentedLagrangianMinimizer (see Constraints).
Factors#
SE2DifferentialDriveFactorBatch(time_steps, wheel_radius, track_width, capacity)Two driven wheels plus passive casters (NVIDIA Carter), kinematic. States: pose \(T_k\) (
SE2StateBatch), wheel speeds \((\omega_L, \omega_R)\) (VectorStateBatch2), pose \(T_{k+1}\). Body twist \(\xi = [r(\omega_R + \omega_L)/2,\ 0,\ r(\omega_R - \omega_L)/b]\); the step is exact for wheel speeds held over the step.SE2KinematicBicycleFactorBatch(time_steps, wheelbase, capacity)Car with Ackermann steering (kinematic bicycle at the rear axle). States: pose \(T_k\), \(z_k = (v, \delta)\), control \(u_k = (a, \dot\delta)\), pose \(T_{k+1}\), \(z_{k+1}\). Body twist \(\xi = [v,\ 0,\ v \tan\delta / L]\); the speed and steering rows are \(z_{k+1} - z_k - \Delta t_k u_k\). Valid at moderate lateral acceleration (no tire slip).
SE3DifferentialDriveFactorBatch,SE3KinematicBicycleFactorBatchThe same vehicles on non-planar terrain (slopes, ramps): the pose is an
SE3StateBatch(vehicle frame x forward, z up) and the wheels set the body twist \([0, 0, \omega, v, 0, 0]\) (tangent order \([\phi, \rho]\)). The residual keeps four of the six components of \(\mathrm{Log}((T_k \mathrm{Exp}(\Delta t_k \xi))^{-1} T_{k+1})\): yaw, forward, lateral (no sideslip) and vertical (no hop). Roll and pitch changes follow the terrain and come from other factors (an attitude or terrain-normal prior, a height map, ground contact); without them they are unobserved.SE2KinematicsFactorBatch,SO3KinematicsFactorBatch,SE3KinematicsFactorBatchLie-group kinematics \(X_{k+1} = X_k \mathrm{Exp}(\Delta t_k \xi_k)\) with the body twist as control (a
VectorStateBatch3/VectorStateBatch6): planar and 3-D camera or robot motion with commanded velocities, attitude kinematics. Exact for a twist held over the step.QuadrotorFactorBatch(time_steps, parameters, capacity)Quadrotor rigid body with the four rotor thrusts as controls. States: pose (
SE3StateBatch), world velocity, body rates, thrusts (VectorStateBatch4), then the next pose, velocity and rates. Euler step of \(\dot p = v\), \(\dot R = R \omega^\wedge\), \(\dot v = -g e_3 + R e_3 \sum f_i / m - D v\), \(J \dot\omega = \tau(f) - \omega \times J\omega\), with the X-layout allocation ofQuadrotorParameters. Needs short steps (10-25 ms).QuadrupedFactorBatch(time_steps, contacts, foot_positions, parameters, capacity)Quadruped base as a single rigid body driven by the four foot forces (world frame,
VectorStateBatch12): \(m \dot v = \sum s_i f_i - m g e_3\), \(J \dot\omega + \omega \times J\omega = R^\top \sum s_i (p_i - p) \times f_i\). The contact flags \(s_i\) and foot positions \(p_i\) are per-step inputs from the gait planner. Contact constraints (normal force, friction cone, zero force in swing) are separate constraint factors.
time_steps is a device buffer of one step duration per factor.
Example: a Carter drives to a goal#
import cupy as cp, numpy as np, pycunls
steps, dt = 30, 0.1
poses = cp.asarray(np.tile(np.eye(3, dtype=np.float32), (steps + 1, 1, 1)))
u = cp.zeros((steps, 2), dtype=cp.float32)
x = pycunls.SE2StateBatch(poses, steps + 1, cp.asarray([0], dtype=cp.int32), 1)
x.set_num_active_states(steps + 1, 1) # x_0 is fixed
uu = pycunls.VectorStateBatch2(u, steps)
uu.set_num_active_states(steps)
dts = cp.full(steps, dt, dtype=cp.float32)
dyn = pycunls.SE2DifferentialDriveFactorBatch(dts, 0.125, 0.5, steps) # r, b
dyn.set_num_active_factors(steps)
hard = pycunls.ConstraintFactorBatch(dyn, pycunls.ConstraintKind.Equality)
# ... goal: SE2 prior on the last pose wrapped as an equality,
# effort: weighted vector prior on u, limits: BoundFactorBatch2 on u.
problem = pycunls.Problem()
problem.add_state_batch(x)
problem.add_state_batch(uu)
ptrs = []
for k in range(steps):
ptrs += [x.state_device_ptr(k), uu.state_device_ptr(k), x.state_device_ptr(k + 1)]
problem.add_factor_batch(hard, ptrs)
# problem.add_factor_batch(goal / effort / limits ...)
pycunls.AugmentedLagrangianMinimizer(pycunls.GaussNewtonMinimizer()).minimize(
pycunls.CudaStream(), problem)