Simulating with DifferentialEquations.jl

Getting started

To simulate the dynamics of the system, DifferentialEquations.jl can be used. The extension VMRobotControlDifferentialEquationsExt.jl is loaded when both VMRobotControl.jl and DifferentialEquation.jl are loaded.

using DifferentialEquations
using VMRobotControl
using StaticArrays

DifferentialEquations.jl expects a function with the signature f(du, u, p, t), where u is the state, t is the current time, p is an optional vector of parameters (which we can ignore), and the answer is returned by modifying du in-place, to hold the derivative of u. The state is composed of the configurations and velocities of each joint, and the du is composed of the velocities and accelerations.

The function get_ode_dynamics(dynamics_cache, gravity; [f_setup, f_control]) returns a function with the correct call signature. The dynamics cache will be mutated while simulating, so take care to avoid memory aliasing if your are multithreading. The optional functions f_setup and f_control are called before starting and at every time step respectively. Their type signatures can be seen by looking at the help for DEFAULT_F_SETUP and DEFAULT_F_CONTROL.

get_ode_dynamics works the same way for mechanisms and virtual mechanism systems.

mechanism = parseRSON("../../../RSONs/rsons/franka_panda/pandaSurgical.rson")

m = compile(mechanism)
dcache = new_dynamics_cache(m)
gravity = SVector{3, Float64}(0.0, 0.0, -10.0)

f_dynamics = get_ode_dynamics(dcache, gravity;)
(::VMRobotControl.var"#ode_dynamics!#226"{typeof(VMRobotControl.DEFAULT_F_CONTROL), VMRobotControl.MechanismCacheBundle{CompiledMechanism{Float64, VMRobotControl.RBTree{Float64, VMRobotControl.TypeStableCollections.TypeStableCollection{Tuple{Vector{CompiledMechanismJoint(Float64, RevoluteData{Float64})}, Vector{CompiledMechanismJoint(Float64, Rigid{Float64})}}}, VMRobotControl.TypeStableCollections.TypeStableCollection{Tuple{Vector{CompiledCoord{JointSubspace{VMRobotControl.CompiledJointID{CompiledMechanismJoint(Float64, RevoluteData{Float64})}}}}, Vector{CompiledCoord{FrameAngularVelocity{CompiledFrameID}}}, Vector{CompiledCoord{FramePoint{Float64, CompiledFrameID}}}}}}, VMRobotControl.TypeStableCollections.TypeStableCollection{Tuple{Vector{Visual{GeometryBasics.Mesh{3, Float32, GeometryBasics.TriangleP{3, Float32, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, GeometryBasics.FaceView{GeometryBasics.TriangleP{3, Float32, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, GeometryBasics.TriangleFace{GeometryBasics.ZeroIndex{Int32}}, StructArrays.StructVector{GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, @NamedTuple{position::Vector{Point{3, Float32}}, normals::Vector{Vec{3, Float32}}}, Int64}, Vector{GeometryBasics.TriangleFace{GeometryBasics.ZeroIndex{Int32}}}}}, CompiledFrameID}}, Vector{Visual{GeometryBasics.Mesh{3, Float32, GeometryBasics.TriangleP{3, Float32, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, GeometryBasics.FaceView{GeometryBasics.TriangleP{3, Float32, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}}, GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}, StructArrays.StructVector{GeometryBasics.PointMeta{3, Float32, Point{3, Float32}, (:normals,), Tuple{Vec{3, Float32}}}, @NamedTuple{position::Vector{Point{3, Float32}}, normals::Vector{Vec{3, Float32}}}, Int64}, Vector{GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}}, CompiledFrameID}}, Vector{Inertia{Float64, CompiledCoordID{FrameAngularVelocity{CompiledFrameID}}}}, Vector{PointMass{Float64, CompiledCoordID{FramePoint{Float64, CompiledFrameID}}}}}}}, VMRobotControl.MechDynamicsCache{Float64}}, Nothing, Vector{Float64}, Vector{Float64}, Base.RefValue{Float64}, UnitRange{Int64}, UnitRange{Int64}}) (generic function with 1 method)

Once we have the dynamics function, we can make an ODE problem, but we need to define the initial state vector. We can use two functions to convert between q, and a state vector u: assemble_state and state_idxs. The usage of these functions differs slightly depending on if you are working with a mechanism or virtual mechanism system, as you will have an extra configuration and velocity vector for the virtual mechanism if you are working with a virtual mechanism system.

q = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
q̇ = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
tspan = 5.0

u0 = assemble_state(m, q, q̇)
prob = ODEProblem(f_dynamics, u0, tspan)
sol = solve(prob, Tsit5())
sol.retcode
ReturnCode.Success = 1

Alternatively you can the step of defining f_dynamics, and use the function get_ode_problem from to do everything in one go:

prob = get_ode_problem(dcache, gravity, q, q̇, tspan)
sol = solve(prob, Tsit5())
sol.retcode
ReturnCode.Success = 1

Using f_setup and f_control