Rail Robot

This example demonstrates a simple rail robot with a Rail joint and a Revolute joint. The resulting robot is a pendulum that can slide along a curved path.

using
    DifferentialEquations,
    GLMakie,
    Random,
    StaticArrays,
    VMRobotControl
using VMRobotControl.Splines: CubicSpline

We use a cubic spline to define the path of the rail robot, and build the mechanism from a Rail joint, a Revolute joint, and a Rigid joint.

spline_knots = [
    -1.0  0.0  0.1;
    -0.5  0.0  0.0;
    -0.0  0.0  0.1;
    0.5  0.0  0.0;
    1.0  0.0  0.1
]
spline = CubicSpline(spline_knots)

T1 = Transform(SVector(0.0, 0.0, 1.0), zero(Rotor{Float64}))
J1 = Rail(spline, T1)

T2 = Transform(SVector(0.0, 0.0, 0.0), zero(Rotor{Float64}))
J2 = Revolute(SVector(0.0, 1.0, 0.0), T2)

T3 = Transform(SVector(0.0, 0.0, 1.0), zero(Rotor{Float64}))
J3 = Rigid(T3)

mech = Mechanism{Float64}("2Link")
cart_frame = add_frame!(mech, "Cart")
L2_frame = add_frame!(mech, "L2")
EE_frame = add_frame!(mech, "EE")

add_joint!(mech, J1; parent="root_frame",   child=cart_frame,               id="J1")
add_joint!(mech, J2; parent=cart_frame,     child=L2_frame,                 id="J2")
add_joint!(mech, J3, parent=L2_frame,       child=EE_frame;                 id="J3")
"J3"

Then, we add a coordinate to the mechanism to represent the tip of the pendulum, and a coordinate to represent the position of the cart. These are used to add point masses to the mechanism.

add_coordinate!(mech, FrameOrigin(EE_frame);                                id="tip_pos")
add_coordinate!(mech, FrameOrigin(cart_frame);                              id="cart_pos")
add_component!(mech, PointMass(1.0, "cart_pos");                            id="cart_mass")
add_component!(mech, PointMass(1.0, "tip_pos");                             id="pendulum_mass")
"pendulum_mass"

We compile the mechanism, and setup an ODE problem to simulate the dynamics of the rail robot.

m = compile(mech)

rng = MersenneTwister(1234)
sol, q, q̇, T = let
    q = [2.0 + 1e-3*randn(rng), 0.0]
    q̇ = [0.0, 0.0]
    T = 30
    prob = get_ode_problem(new_dynamics_cache(m), VMRobotControl.DEFAULT_GRAVITY, q, q̇, T)
    sol = solve(prob, Tsit5(), maxiters=1e4, abstol=1e-6, reltol=1e-6)
    sol, q, q̇, T
end;

Finally, we animate the solution of the ODE problem to visualize the rail robot. We will create a vector of all the tip positions of the pendulum, to animate a trail.

fig = Figure(; size=(720, 720), figure_padding=0)
display(fig)
ls = LScene(fig[1, 1]; show_axis=false)
cam = cam3d!(ls; center=false)
cam.lookat[] = [0.0, 0.0, 0.5]
cam.eyeposition[] = [0.0, 5.0, 1.0]
q_obs = Observable(q)
N_trail_points = 60
trail_points = Observable(Vector{SVector{3, Float64}}(undef, N_trail_points))
fill!(trail_points[], SVector{3, Float64}(NaN, NaN, NaN))
trail_colors = 1:N_trail_points
trail_width = 0.5*exp.(-LinRange(0, -3, N_trail_points))
cache = Observable(new_kinematics_cache(m))
robotsketch!(ls, cache; linewidth=3)
lines!(ls, trail_points; color=trail_colors, colormap=:viridis, linewidth=trail_width)

animate_f_setup(cache) = (get_compiled_coordID(m, "tip_pos"),)
function animate_f_control(cache, t, args, extra)
    tip_pos_ID = args[1]
    pos = configuration(cache, tip_pos_ID)
    trail_points[][1:N_trail_points-1] .= trail_points[][2:N_trail_points]
    trail_points[][N_trail_points] = pos
    cam.eyeposition[] = [2.0*sin(0.2*t), 5.0, 1.0]
    update_cam!(ls.scene, cam)
    notify(trail_points)
end


module_path = joinpath(splitpath(splitdir(pathof(VMRobotControl))[1])[1:end-1])
savepath = joinpath(module_path, "docs/src/assets/rail_robot.mp4")
animate_robot_odesolution(fig, sol, cache, savepath; f_setup=animate_f_setup, f_control=animate_f_control)
"/home/runner/work/VMRobotControl.jl/VMRobotControl.jl/docs/src/assets/rail_robot.mp4"

This page was generated using Literate.jl.