Skip to content

Commit 4bd60bd

Browse files
committed
Adds CompiledVisual type, and changes Visual, for better parsing/saving of rsons/urdfs. Retains filename if mesh for a visual came from a file. Some minor updates to RNE related code.
1 parent cd5e647 commit 4bd60bd

File tree

13 files changed

+867
-93
lines changed

13 files changed

+867
-93
lines changed

RSONs/rsons/franka_panda/calibrated_frodo.rson

Lines changed: 684 additions & 0 deletions
Large diffs are not rendered by default.

ext/VMRobotControlMakieExt.jl

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ using VMRobotControl:
1919
CompiledFrameID,
2020
CompiledJointID,
2121
CompiledCoordID,
22+
CompiledVisual,
2223
VMSFrameID,
2324
VMSCoordID
2425
using VMRobotControl:
@@ -503,21 +504,16 @@ function Makie.plot!(plot::RobotVisualize{Tuple{C}}) where C<:MechanismCacheBund
503504
frames = CompiledFrameID[]
504505

505506
foreach(vis) do v
506-
# geom = normal_mesh(v.geometry)
507-
geom = v.geometry
508-
if isa(geom, Mesh)
509-
msh = Makie.mesh!(plot, geom;
507+
v::CompiledVisual
508+
msh = Makie.mesh!(plot, v.mesh;
510509
shading=plot.shading,
511510
color=v.color,
512511
specular=v.specular,
513512
shininess=v.shininess,
514513
transparency=plot.transparency
515-
)
516-
push!(meshes, msh)
517-
push!(frames, v.frame)
518-
else
519-
error("Unsupported geometry type: $(typeof(geom))")
520-
end
514+
)
515+
push!(meshes, msh)
516+
push!(frames, v.frame)
521517
end
522518

523519
function update_from_cache(cache)

src/VMRobotControl.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ import DiffResults:
224224
import
225225
GeometryBasics # Use import to avoid name conflicts
226226

227+
using GeometryBasics:
228+
normal_mesh
229+
227230
using
228231
DigitalAssetExchangeFormatIO,
229232
FileIO,

src/compiled_mechanisms.jl

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,14 @@ function _compile_component!(
454454
cd1 = reassign_frames(component_data, frame_id_map)
455455
cd2 = reassign_joints(cd1, joint_id_map)
456456
cd3 = reassign_coords(cd2, coord_id_map)
457-
cc, idx = extend(cc, cd3)
457+
cd4 = isa(cd3, Visual) ? compile_visual(cd3) : cd3 # this may return a vector of compiled visuals
458+
if isa(cd4, AbstractVector)
459+
for cd in cd4
460+
cc, idx = extend(cc, cd)
461+
end
462+
else
463+
cc, idx = extend(cc, cd4)
464+
end
458465
ret_component_id_map[id] = CompiledComponentID(idx)
459466
cc
460467
end
@@ -513,7 +520,7 @@ inertances(m::CompiledMechanism) = filtertype(m.components, Inertance)
513520
storages(m::CompiledMechanism) = filtertype(m.components, Storage)
514521
dissipations(m::CompiledMechanism) = filtertype(m.components, Dissipation)
515522
generic_components(m::CompiledMechanism) = filtertype(m.components, GenericComponent)
516-
visuals(m::CompiledMechanism) = filtertype(m.components, Visual)
523+
visuals(m::CompiledMechanism) = filtertype(m.components, CompiledVisual)
517524

518525

519526
compile(m::Mechanism) = CompiledMechanism(m)
@@ -538,15 +545,15 @@ get_compiled_coordID(m::CompiledMechanism, c::String) = get_compiled_coordID(m.r
538545
get_compiled_componentID(m::CompiledMechanism, c::String) = m.component_id_map[c]
539546

540547
get_compiled_joint(m::CompiledMechanism, jointID::CompiledJointID) = joints(m)[jointID.idx]
541-
get_compiled_coord(m::CompiledMechanism, coordID::CompiledCoordID) = coordinates(m)[coordID]
542-
get_compiled_component(m::CompiledMechanism, componentID::CompiledComponentID) = components(m)[componentID]
548+
get_compiled_coord(m::CompiledMechanism, coordID::CompiledCoordID) = coordinates(m)[coordID.depth][coordID.idx]
549+
get_compiled_component(m::CompiledMechanism, componentID::CompiledComponentID) = components(m)[componentID.idx]
543550

544551
Base.getindex(m::CompiledMechanism, id::CompiledJointID) = get_compiled_joint(m, id)
545552
Base.getindex(m::CompiledMechanism, id::CompiledCoordID) = get_compiled_coord(m, id)
546553
Base.getindex(m::CompiledMechanism, id::CompiledComponentID) = get_compiled_component(m, id)
547554

548555
Base.setindex!(m::CompiledMechanism, val::J, id::CompiledJointID{J}) where J = joints(m)[id.idx] = val
549-
Base.setindex!(m::CompiledMechanism, val::C, id::CompiledCoordID{C}) where C = coordinates(m)[id.idx] = val
556+
Base.setindex!(m::CompiledMechanism, val::C, id::CompiledCoordID{C}) where C = coordinates(m)[id.depth][id.idx] = val
550557
Base.setindex!(m::CompiledMechanism, val::C, id::CompiledComponentID{C}) where C = components(m)[id.idx] = val
551558

552559
get_force_components(m::CompiledMechanism) = filtertype(components(m), Union{Storage, Dissipation, Inertance, GenericComponent})

src/components/components.jl

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -378,41 +378,96 @@ end
378378

379379
#####################################################
380380

381+
struct MeshFromFile{M} # used for urdf/rson parsing/serializing
382+
mesh::M
383+
path::String
384+
end
385+
381386
struct Visual{G, FID} <: ComponentData{Float64}
382387
frame::FID
388+
transform::Union{Nothing, Transform{Float64}}
383389
geometry::G
384390
color::RGBAf
385391
specular::Float32
386392
shininess::Float32
387-
function Visual(frame, geometry; color=RGBAf(1.0f0, 0.0f0, 1.0f0, 1.0f0), specular=0.2f0, shininess=32.0f0)
393+
function Visual(frame, transform, geometry; color=RGBAf(1.0f0, 0.0f0, 1.0f0, 1.0f0), specular=0.2f0, shininess=32.0f0)
388394
if isa(geometry, GeometryBasics.GeometryPrimitive)
389-
_mesh = GeometryBasics.normal_mesh(geometry)
395+
# _mesh = GeometryBasics.normal_mesh(geometry)
396+
new{typeof(geometry), typeof(frame)}(frame, transform, geometry, color, specular, shininess)
390397
elseif isa(geometry, GeometryBasics.Mesh)
398+
_mesh = GeometryBasics.normal_mesh(geometry)
399+
new{typeof(_mesh), typeof(frame)}(frame, transform, _mesh, color, specular, shininess)
400+
elseif isa(geometry, MeshFromFile{<:GeometryBasics.Mesh})
391401
_mesh = geometry
402+
new{typeof(_mesh), typeof(frame)}(frame, transform, _mesh, color, specular, shininess)
403+
elseif isa(geometry, MeshFromFile{DAEScene})
404+
new{typeof(geometry), typeof(frame)}(frame, transform, geometry, color, specular, shininess)
392405
else
393-
error("Invalid geometry type: $(typeof(geometry))")
406+
error("Unrecognized geometry type: $(typeof(geometry))")
394407
end
395-
new{typeof(_mesh), typeof(frame)}(frame, _mesh, color, specular, shininess)
396408
end
397409

398-
function Visual(name, color, geometry)
399-
# legacy constructor
400-
Visual(name, geometry; color=color)
401-
end
410+
# function Visual(name, color, geometry)
411+
# # legacy constructor
412+
# Visual(name, geometry; color=color)
413+
# end
402414

403-
function Visual(name, frame, color, geometry)
404-
@warn "Visual(name, frame, color, geometry) is deprecated. Use Visual(frame, color, geometry) instead."
405-
Visual(frame, color, geometry)
406-
end
407-
# Keyword constructor
408-
Visual(; frame, geometry, color, specular, shininess) = Visual(frame, geometry; color=color, specular=specular, shininess=shininess)
415+
# function Visual(name, frame, color, geometry)
416+
# @warn "Visual(name, frame, color, geometry) is deprecated. Use Visual(frame, color, geometry) instead."
417+
# Visual(frame, color, geometry)
418+
# end
419+
# # Keyword constructor
420+
Visual(; frame, transform, geometry, color, specular, shininess) = Visual(frame, transform, geometry; color, specular, shininess)
409421
end
410422

411423
function Base.:*(tf::Transform, v::Visual)
412-
Visual(v.frame, v.color, VMRobotControl.transform_mesh(v.geometry, tf))
424+
if isnothing(v.transform)
425+
remake(v; transform=tf)
426+
else
427+
remake(v; transform=tf*v.transform)
428+
end
413429
end
414430

415-
416431
_reassign_frames(c::Visual, frd) = remake(c; frame=get_compiled_frameID(frd, c.frame))
417432
_reassign_joints(c::Visual, jrd) = c
418433
_reassign_coords(c::Visual, crd) = c
434+
435+
struct CompiledVisual{M<:GeometryBasics.Mesh} <:ComponentData{Float64}
436+
frame::FrameID
437+
mesh::M
438+
color::RGBAf
439+
specular::Float32
440+
shininess::Float32
441+
end
442+
443+
function compile_visual(v::Visual)
444+
if isa(v.geometry, MeshFromFile{DAEScene})
445+
# If the geometry is a DAE scene, load it
446+
meshes_kwargs = convert_for_glmakie(v.geometry.mesh)
447+
return [
448+
begin
449+
color = haskey(kwargs, :color) ? kwargs[:color] : v.color
450+
specular = haskey(kwargs, :specular) ? kwargs[:specular] : v.specular
451+
shininess = haskey(kwargs, :shininess) ? kwargs[:shininess] : v.shininess
452+
_mesh = convert(GeometryBasics.GLNormalMesh{3}, mesh)
453+
transformed_mesh = VMRobotControl.transform_mesh(_mesh, v.transform)
454+
M = typeof(transformed_mesh)
455+
CompiledVisual{M}(v.frame, transformed_mesh, color, specular, shininess)
456+
end
457+
for (mesh, kwargs) in meshes_kwargs
458+
]
459+
end
460+
461+
if isa(v.geometry, MeshFromFile{<:GeometryBasics.Mesh})
462+
# If the geometry is a mesh from file, load it
463+
mesh = normal_mesh(v.geometry.mesh)
464+
elseif isa(v.geometry, GeometryBasics.GeometryPrimitive)
465+
# Otherwise, use the geometry as is
466+
mesh = normal_mesh(v.geometry)
467+
elseif isa(v.geometry, GeometryBasics.Mesh)
468+
mesh = normal_mesh(v.geometry)
469+
else
470+
error("Unrecognized geometry type: $(typeof(v.geometry))")
471+
end
472+
CompiledVisual(v.frame, mesh, v.color, v.specular, v.shininess)
473+
end

src/interface.jl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function compute_in_coordinate_order(f, coords::Vector{<:TypeStableCollection})
3737
for tsc in coords
3838
foreach(tsc) do coord
3939
f(coord)
40+
nothing
4041
end
4142
end
4243
nothing
@@ -751,7 +752,18 @@ See also [`dynamics!`](@ref)
751752
function new_dynamics_cache end
752753

753754

754-
# TODO
755+
"""
756+
new_inverse_dynamics_cache(m[, T])
757+
758+
Creates a cache with element type `::T` (defaulting to the eltype of the mechanism) for performing
759+
inverse dynamics computations with the Recursive Newton Euler (RNE) algorithm on a compiled mechanism.
760+
761+
This cache supports computation of frame transforms, velocities, velocity-product-accelerations.
762+
It also supports [`configuration`](@ref), [`velocity`](@ref), [`acceleration`](@ref), but
763+
not [`jacobian`](@ref) for coordinates.
764+
765+
See also [`dynamics!`](@ref)
766+
"""
755767
function new_inverse_dynamics_cache end
756768

757769
"""

src/joints/joint_definitions.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,12 +623,23 @@ function _project_forces_to_jointspace(j::RevoluteData, tf_p::Transform{T}, tf_c
623623

624624
# The minus sign is due to the fact that the torque is expressed on the right side of the
625625
# equation
626-
u = -dot(rotor(tf_p) * rotor(j.transform) * j.axis, τ_c)
626+
axis = rotor(tf_p) * rotor(j.transform) * j.axis
627+
u = -dot(axis, τ_c)
627628
return SVector(u)
628629
end
629630

630631
function _project_forces_to_jointspace(j::PrismaticData, tf_p::Transform{T}, tf_c::Transform{T}, f_p::S, f_c::S, τ_p::S, τ_c::S) where {T, S<: SVector{3, T}}
631632
# Rotate axis to world frame, then dot with force
632-
u = -dot(rotor(tf_c) * j.axis, f_c)
633+
axis = rotor(tf_c) * j.axis
634+
u = -dot(axis, f_c)
635+
return SVector(u)
636+
end
637+
638+
function _project_forces_to_jointspace(j::RailData, tf_p::Transform{T}, tf_c::Transform{T}, f_p::S, f_c::S, τ_p::S, τ_c::S) where {T, S<: SVector{3, T}}
639+
# Rotate axis to world frame, then dot with force
640+
# TODO test
641+
dspline = spline_derivative(q[1]/joint.scaling, joint.spline)/joint.scaling
642+
axis = rotor(tfₚ) * rotor(joint.transform) * dspline
643+
u = -dot(axis, f_c)
633644
return SVector(u)
634645
end

src/rson/parsing.jl

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -508,15 +508,15 @@ function parseVisual!(mechanism, name::JSON_STR, data, cfg::RSONParserConfig)
508508
expect = (field, TYPE) -> expectField(data, field, TYPE, cfg)
509509
if type == "box"
510510
widths = parseVec(expect("size", Vector{Any}), Float64, Val{3}(), cfg)
511-
mesh = normal_mesh(Rect{3, Float64}(-widths./2, widths))
511+
geom = Rect{3, Float64}(-widths./2, widths)
512512
elseif type == "cylinder"
513513
L = expect("length", Float64)
514514
origin = L * SVector(0.0, 0.0, -0.5)
515515
extremity = L * SVector(0.0, 0.0, 0.5)
516-
mesh = Cylinder{Float64}(origin, extremity, expect("radius", Float64))
516+
geom = Cylinder{Float64}(origin, extremity, expect("radius", Float64))
517517
elseif type == "sphere"
518518
origin = SVector(0.0, 0.0, 0.0)
519-
mesh = normal_mesh(Sphere{Float64}(origin, expect("radius", Float64)))
519+
geom = Sphere{Float64}(origin, expect("radius", Float64))
520520
elseif type == "mesh"
521521
if isnothing(cfg.rson_path)
522522
rson_path = "."
@@ -527,27 +527,23 @@ function parseVisual!(mechanism, name::JSON_STR, data, cfg::RSONParserConfig)
527527
mesh_path = expect("filename", String)
528528
path = joinpath(rson_path, mesh_path)
529529
mesh = load_mesh(path, cfg) # This can return multiple meshes
530+
geom = MeshFromFile(mesh, mesh_path) # MeshFromFile is a wrapper for GeometryBasics.Mesh
530531
# visual.scale = expect("scale")
531532
elseif type == "meshdata"
532533
verts = expect("vertices", Vector{Any})
533534
faces = expect("faces", Vector{Any})
534535
# TODO verify that verts and faces are correct types/lengths
535536
verts = [parseVertex(vert, cfg) for vert in verts]
536537
faces = [parseFace(face, cfg) for face in faces]
537-
mesh = GeometryBasics.Mesh(verts, faces)
538+
geom = GeometryBasics.Mesh(verts, faces)
538539
else
539540
rson_throw_exception("Unrecognised visual type '$(type)'", cfg)
540541
end
541542
frame = parseFrameOutsideFramesList(mechanism, expect("frame", Union{String, OrderedDict{String, Any}}), cfg)
542543
color = parseMaterial(expect("material", Union{String, Vector{Any}}), cfg)
543544

544-
if isa(mesh, Vector{<:Tuple{<:Mesh, <:NamedTuple}})
545-
for (i, (mesh, kwargs)) in enumerate(mesh)
546-
add_component!(mechanism, Visual(frame, mesh; kwargs...); id=name*"_$i")
547-
end
548-
else
549-
add_component!(mechanism, Visual(frame, color, mesh); id=name)
550-
end
545+
tf = zero(Transform{Float64})
546+
add_component!(mechanism, Visual(frame, tf, geom; color); id=name)
551547
check_for_remaining_fields(data, cfg)
552548
nothing
553549
else
@@ -561,7 +557,7 @@ function load_mesh(path, cfg)
561557
if isa(ret, GeometryBasics.Mesh)
562558
return ret
563559
elseif isa(ret, DAEScene)
564-
return convert_for_glmakie(ret)
560+
return ret
565561
else
566562
throw("Unhandled mesh type $(typeof(ret))")
567563
end

src/rson/rson.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ using ..VMRobotControl:
2929
RevoluteData,
3030
PrismaticData,
3131
Rigid,
32-
MechanismJoint
32+
MechanismJoint,
33+
MeshFromFile
3334

3435
import JSON
3536

src/rson/serializing.jl

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -283,14 +283,47 @@ end
283283
# Visual
284284

285285
function serialize_component(v::Visual)
286-
@assert v.geometry isa Mesh
287-
verts = GeometryBasics.coordinates(v.geometry)
288-
faces = [Int[face...] for face in GeometryBasics.faces(v.geometry)]
289-
OrderedDict(
290-
"type" => "meshdata",
291-
"frame" => v.frame,
292-
"material" => jsonify(v.color),
293-
"vertices" => verts,
294-
"faces" => faces
295-
)
286+
if v.geometry isa Mesh
287+
@warn "Serializing a mesh directly will create a large rson file."
288+
verts = GeometryBasics.coordinates(v.geometry)
289+
faces = [Int[face...] for face in GeometryBasics.faces(v.geometry)]
290+
OrderedDict(
291+
"type" => "meshdata",
292+
"frame" => v.frame,
293+
"material" => jsonify(v.color),
294+
"vertices" => verts,
295+
"faces" => faces
296+
)
297+
elseif v.geometry isa MeshFromFile
298+
OrderedDict(
299+
"type" => "mesh",
300+
"frame" => v.frame,
301+
"material" => jsonify(v.color),
302+
"filename" => v.geometry.path
303+
)
304+
elseif v.geometry isa Rect
305+
OrderedDict(
306+
"type" => "box",
307+
"frame" => v.frame,
308+
"material" => jsonify(v.color),
309+
"size" => v.geometry.widths
310+
)
311+
elseif v.geometry isa Cylinder
312+
OrderedDict(
313+
"type" => "cylinder",
314+
"frame" => v.frame,
315+
"material" => jsonify(v.color),
316+
"radius" => v.geometry.radius,
317+
"length" => norm(v.geometry.origin - v.origin.extremity)
318+
)
319+
elseif v.geometry isa Sphere
320+
OrderedDict(
321+
"type" => "sphere",
322+
"frame" => v.frame,
323+
"material" => jsonify(v.color),
324+
"radius" => v.geometry.r
325+
)
326+
else
327+
rson_throw_exception("Unrecognised visual type '$(type)'", cfg)
328+
end
296329
end

0 commit comments

Comments
 (0)