Using a Mechanism

Compiling

The Mechanism type is designed to be used for building the mechanism, not for performing fast computations. Its internal data structures rely on Abstract types, which makes construction easier, but is inherently type unstable. Once you have built your mechanism it can be "compiled" into a CompiledMechanism, which is fully type stable (see TypeStableCollection for more info on how this is achieved.).

First, we will load a mechanism: a 3 link robot defined by an RSON file.

using VMRobotControl
mechanism = parseRSON("../../../RSONs/rsons/simple3link.rson")
3DOF Mechanism{Float64} "simple3link" with 13 frames, 12 joints, 6 coordinates, 17 components

Compiling a mechanism is as simple as

m = compile(mechanism)
CompiledMechanism{Float64...}(simple3link)

This returns the type-stable version of your mechanism. The compiled mechanism's type stable data structures can be accessed with fast indices, for example let us consider the coordinate we added earlier for the link 3 centre of mass "L3_com".

L3_com = get_compiled_coordID(m, "L3_com")
CompiledCoordID{FramePoint{Float64, CompiledFrameID}}(1, TypeStableIdx{CompiledCoord{FramePoint{Float64, CompiledFrameID}}}(3))

This returns the CompiledCoordID for the coordinate. Note that VMRobotControl.CompiledCoordID is a parametric type, so its type varies depending on the type of the coordinate. We can use this ID on the mechanism to get the compiled coordinate data:

m[L3_com]
CompiledCoord{FramePoint{Float64, CompiledFrameID}}(FramePoint{Float64, CompiledFrameID}(CompiledFrameID(4), [0.0, 0.0, 0.5]), 9:11)

In this case, the compiled coordinate data has replaced String frame ID "L3_frame" with the compiled frame ID of the compiled frame.

L3_frame_id = get_compiled_frameID(m, "L3_frame")
L3_frame_id == m[L3_com].coord_data.frameID
true

For the full API see API

Caches and "Cache-Bundles"

To use a mechanism to perform computations we need to create a cache for storing intermediate computations and results in. The cache and mechanism are normally needed together, so they are bundled together by default.

There are several functions for creating cache-bundles for different purposes:

ConstructorMethodsDescription
new_kinematics_cachekinematics!For transforms/coordinate configurations, used for plotting
new_jacobians_cachejacobians!For kinematics and jacobians
new_rbstates_cachevelocity_kinematics!, acceleration_kinematics!For kinematics and velocities/velocity product accelerations (VPAs)
new_dynamics_cachevelocity_kinematics!, precompute!, inertance_matrix!, generalized_force!, dynamics!For kinematics, velocities, VPAs, jacobians, and inertance matrices/generalized forces, used for simulating
new_control_cachecontrol_step!For realtime control with a virtual-mechanism-system

For examples of using the kinematics cache for plotting skip to Plotting with Makie. For examples of using the dynamics cache for simulation skip to Simulating with DifferentialEquations.jl.

dcache = new_dynamics_cache(m)

t = 0.0
q = zero_q(m)
q̇ = zero_q̇(m)
g = VMRobotControl.DEFAULT_GRAVITY
u = zero_u(m)
dynamics!(dcache, t, q, q̇, g, u)
3-element Vector{Float64}:
 0.0
 0.0
 0.0

Accessing results

There are several functions for accessing results from a cache/bundle.

Warning

The contents of a cache are NOT guaranteed to be initialized when it is created. Ensure you call the method needed to compute your results before accessing results from the cache.

Warning

Note that some of these results are Vectors/Matrices from the cache, or other heap-allocated objects, that will be updated when you use the cache again. If you are using a result from the cache, and need to use the cache again, make sure to copy the results first!

As we have called dynamics!, the results in the cache are ready to view. Let's look at some of the computed values for the compiled frame ID from before.

julia> get_inertance_matrix(dcache)3×3 Matrix{Float64}:
 8.75  4.5   1.25
 4.5   2.5   0.75
 1.25  0.75  0.25
julia> get_generalized_force(dcache)3-element Vector{Float64}: 0.0 0.0 0.0
julia> get_transform(dcache, L3_frame_id)Transform{Float64}([0.0, 0.0, 2.0], Rotor{Float64}(1.0, [0.0, 0.0, 0.0]))
julia> get_linear_vel(dcache, L3_frame_id)3-element SVector{3, Float64} with indices SOneTo(3): 0.0 0.0 0.0
julia> get_angular_vel(dcache, L3_frame_id)3-element SVector{3, Float64} with indices SOneTo(3): 0.0 0.0 0.0
julia> get_linear_vpa(dcache, L3_frame_id)3-element SVector{3, Float64} with indices SOneTo(3): 0.0 0.0 0.0
julia> get_angular_vpa(dcache, L3_frame_id)3-element SVector{3, Float64} with indices SOneTo(3): 0.0 0.0 0.0
julia> get_linear_jacobian(dcache, L3_frame_id)3×3 view(::Array{Float64, 3}, 1:3, :, 4) with eltype Float64: 2.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
julia> get_angular_jacobian(dcache, L3_frame_id)3×3 view(::Array{Float64, 3}, 4:6, :, 4) with eltype Float64: 0.0 0.0 0.0 1.0 1.0 1.0 0.0 0.0 0.0

However, more often than not you will instead use coordinates to access positions/velocities and jacobians of interest.

Coordinate computations

There are four functions to access computation results for coordinates: configuration, velocity, vpa, and jacobian. As before, you must make sure the cache is populated with results before calling these functions, and copy the result elsewhere if you are then going to use the cache again.

julia> configuration(dcache, L3_com)3-element SVector{3, Float64} with indices SOneTo(3):
 0.0
 0.0
 2.5
julia> velocity(dcache, L3_com)3-element SVector{3, Float64} with indices SOneTo(3): 0.0 0.0 0.0
julia> vpa(dcache, L3_com)3-element SVector{3, Float64} with indices SOneTo(3): 0.0 0.0 0.0
julia> jacobian(dcache, L3_com)3×3 view(::Matrix{Float64}, 9:11, :) with eltype Float64: 2.5 1.5 0.5 0.0 0.0 0.0 0.0 0.0 0.0

Configuration is dependent only on the time and joint state q, so can also be used with the kinematics cache.

Component computations

TODO

Modifying a compiled mechanism

A compiled mechanism can be modified using Base.setindex!, by modifying its joints/coordinates/components, only if the type of the joint/component does not change. This is made simpler by using the remake function which allows you to easily remake a compiled joint/coordinate/component and modify one or more of its fields using keyword arguments. The interface to do so is like so:

using VMRobotControl: remake
m[ee_com] = remake(m[ee_com]; point=SVector(0.3*z))