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:
Constructor | Methods | Description |
---|---|---|
new_kinematics_cache | kinematics! | For transforms/coordinate configurations, used for plotting |
new_jacobians_cache | jacobians! | For kinematics and jacobians |
new_rbstates_cache | velocity_kinematics! , acceleration_kinematics! | For kinematics and velocities/velocity product accelerations (VPAs) |
new_dynamics_cache | velocity_kinematics! , precompute! , inertance_matrix! , generalized_force! , dynamics! | For kinematics, velocities, VPAs, jacobians, and inertance matrices/generalized forces, used for simulating |
new_control_cache | control_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.
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.
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))