pagoda.cooper.World

class pagoda.cooper.World(dt=0.016666666666666666, max_angular_speed=20)[source]

Simulate a physics world that includes an articulated skeleton model.

The “cooper” method, originally described by Cooper & Ballard (2012 Proc. Motion in Games), uses a forward physics simulator (here, the Open Dynamics Engine; ODE) to compute inverse motion quantities like angles and torques using motion-capture data and a structured, articulated model of the human skeleton. The prerequisites for this method are:

  • Record some motion-capture data from a human. This is expected to result in the locations, in world coordinates, of several motion-capture markers at regularly-spaced intervals over time.
  • Construct a simulated skeleton that matches the size and shape of the human to some reasonable degree of accuracy. The more accurate the skeleton, the more accurate the resulting measurements.

In broad strokes, the cooper method proceeds in two stages:

  1. Inverse Kinematics. The motion-capture data are attached to the simulated skeleton using ball joints. These ball joints are configured so that their constraints (namely, placing both anchor points of the joint at the same location in space) are allowed to slip; ODE implements this slippage using a spring dynamics, which provides a natural mechanism for the articulated skeleton to interpolate the marker data as well as possible.

    At each frame during the first pass, the motion-capture markers are placed at the appropriate location in space, and the attached articulated skeleton “snaps” toward the markers using its inertia (from the motion in preceding frames) as well as the spring constraints provided by the marker joint slippage.

    At each frame of this process, the articulated skeleton can be queried to obtain joint angles for each degree of freedom. In addition, the markers can be queried to find their constraint slippage.

  2. Inverse Dynamics. The marker constraints are removed, and the joint angles computed in the first pass are used to constrain the skeleton’s movements.

    At each frame during the second pass, the joints in the skeleton attempt to follow the angles computed in the first pass; a PID controller is used to convert the angular error value into a target angular velocity for each joint.

    The torques that ODE computes to solve this forward angle-following problem are returned as a result of the second pass.

In general, the cooper model is a useful way of getting a physics simulator, a model of a human skeleton, and some motion-capture data to interact smoothly. Particularly useful for almost any simulations of human motion are the settle_to_markers() and follow_markers() methods.

__init__(dt=0.016666666666666666, max_angular_speed=20)[source]

Methods

__init__([dt, max_angular_speed])
are_connected(body_a, body_b) Determine whether the given bodies are currently connected.
create_body(shape[, name]) Create a new body.
follow_markers([start, end, states]) Iterate over a set of marker data, dragging its skeleton along.
forward_dynamics(torques[, start, states]) Move the body according to a set of torque data.
get_body(key) Get a body by key.
get_body_states() Return the complete state of all bodies in the world.
get_joint(key) Get a joint by key.
inverse_dynamics(angles[, start, end, ...]) Follow a set of angle data, yielding dynamic joint torques.
inverse_kinematics([start, end, states, ...]) Follow a set of marker data, yielding kinematic joint angles.
join(shape, body_a[, body_b, name]) Create a new joint that connects two bodies together.
load_markers(filename, attachments[, max_frames]) Load marker data and attachment preferences into the model.
load_skeleton(filename[, pid_params]) Create and configure a skeleton in our model.
move_next_to(body_a, body_b, offset_a, offset_b) Move one body to be near another one.
needs_reset() Return True iff the world needs to be reset.
on_collision(args, geom_a, geom_b) Callback function for the collide() method.
on_key_press(key, modifiers, keymap) Handle an otherwise unhandled keypress event (from a GUI).
reset() Reset the automatic process that gets called by step().
set_body_states(states) Set the states of some bodies in the world.
settle_to_markers([frame_no, max_distance, ...]) Settle the skeleton to our marker data at a specific frame.
step([substeps]) Advance the physics world by one step.

Attributes

bodies Sequence of all bodies in the world, sorted by name.
cfm Current global CFM value.
erp Current global ERP value.
gravity Current gravity vector in the world.
joints Sequence of all joints in the world, sorted by name.
follow_markers(start=0, end=1e+100, states=None)[source]

Iterate over a set of marker data, dragging its skeleton along.

Parameters:

start : int, optional

Start following marker data after this frame. Defaults to 0.

end : int, optional

Stop following marker data after this frame. Defaults to the end of the marker data.

states : list of body states, optional

If given, set the states of the skeleton bodies to these values before starting to follow the marker data.

forward_dynamics(torques, start=0, states=None)[source]

Move the body according to a set of torque data.

inverse_dynamics(angles, start=0, end=1e+100, states=None, max_force=100)[source]

Follow a set of angle data, yielding dynamic joint torques.

Parameters:

angles : ndarray (num-frames x num-dofs)

Follow angle data provided by this array of angle values.

start : int, optional

Start following angle data after this frame. Defaults to the start of the angle data.

end : int, optional

Stop following angle data after this frame. Defaults to the end of the angle data.

states : list of body states, optional

If given, set the states of the skeleton bodies to these values before starting to follow the marker data.

max_force : float, optional

Allow each degree of freedom in the skeleton to exert at most this force when attempting to follow the given joint angles. Defaults to 100N. Setting this value to be large results in more accurate following but can cause oscillations in the PID controllers, resulting in noisy torques.

Returns:

torques : sequence of torque frames

Returns a generator of joint torque data for the skeleton. One set of joint torques will be generated for each frame of angle data between start and end.

inverse_kinematics(start=0, end=1e+100, states=None, max_force=20)[source]

Follow a set of marker data, yielding kinematic joint angles.

Parameters:

start : int, optional

Start following marker data after this frame. Defaults to 0.

end : int, optional

Stop following marker data after this frame. Defaults to the end of the marker data.

states : list of body states, optional

If given, set the states of the skeleton bodies to these values before starting to follow the marker data.

max_force : float, optional

Allow each degree of freedom in the skeleton to exert at most this force when attempting to maintain its equilibrium position. This defaults to 20N. Set this value higher to simulate a stiff skeleton while following marker data.

Returns:

angles : sequence of angle frames

Returns a generator of joint angle data for the skeleton. One set of joint angles will be generated for each frame of marker data between start and end.

load_markers(filename, attachments, max_frames=1e+100)[source]

Load marker data and attachment preferences into the model.

Parameters:

filename : str

The name of a file containing marker data. This currently needs to be either a .C3D or a .CSV file. CSV files must adhere to a fairly strict column naming convention; see Markers.load_csv() for more information.

attachments : str

The name of a text file specifying how markers are attached to skeleton bodies.

max_frames : number, optional

Only read in this many frames of marker data. By default, the entire data file is read into memory.

Returns:

markers : Markers

Returns a markers object containing loaded marker data as well as skeleton attachment configuration.

load_skeleton(filename, pid_params=None)[source]

Create and configure a skeleton in our model.

Parameters:

filename : str

The name of a file containing skeleton configuration data.

pid_params : dict, optional

If given, use this dictionary to set the PID controller parameters on each joint in the skeleton. See pagoda.skeleton.pid() for more information.

reset()[source]

Reset the automatic process that gets called by step().

By default this follows whatever marker data is loaded into our model.

Provide an override for this method to customize the default behavior of the step() method.

settle_to_markers(frame_no=0, max_distance=0.05, max_iters=300, states=None)[source]

Settle the skeleton to our marker data at a specific frame.

Parameters:

frame_no : int, optional

Settle the skeleton to marker data at this frame. Defaults to 0.

max_distance : float, optional

The settling process will stop when the mean marker distance falls below this threshold. Defaults to 0.1m (10cm). Setting this too small prevents the settling process from finishing (it will loop indefinitely), and setting it too large prevents the skeleton from settling to a stable state near the markers.

max_iters : int, optional

Attempt to settle markers for at most this many iterations. Defaults to 1000.

states : list of body states, optional

If given, set the bodies in our skeleton to these kinematic states before starting the settling process.

step(substeps=2)[source]

Advance the physics world by one step.

Typically this is called as part of a pagoda.viewer.Viewer, but it can also be called manually (or some other stepping mechanism entirely can be used).