Skip to content

Commit 166d3d0

Browse files
committed
Merge remote-tracking branch 'JuliaRobotics/master' into hf/wrenches-cache
2 parents 94034b2 + b9ef1d6 commit 166d3d0

9 files changed

+160
-6
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "RigidBodyDynamics"
22
uuid = "366cf18f-59d5-5db9-a4de-86a9f6786172"
3-
version = "2.3.1"
3+
version = "2.3.2"
44

55
[deps]
66
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"

src/RigidBodyDynamics.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ export
8888
replace_joint!,
8989
maximal_coordinates,
9090
submechanism,
91-
remove_fixed_tree_joints!
91+
remove_fixed_tree_joints!,
92+
remove_subtree!
9293

9394
# contact-related functionality
9495
export # note: contact-related functionality may be changed significantly in the future

src/graphs/Graphs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export
3939
tree_index,
4040
ancestors,
4141
lowest_common_ancestor,
42+
subtree_vertices,
4243
direction,
4344
directions
4445

src/graphs/spanning_tree.jl

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ function SpanningTree(g::DirectedGraph{V, E}, root::V, flipped_edge_map::Union{A
8282
SpanningTree(g, root, tree_edges)
8383
end
8484

85-
# adds an edge and vertex to both the tree and the underlying graph
85+
"""
86+
Add an edge and vertex to both the tree and the underlying graph.
87+
"""
8688
function add_edge!(tree::SpanningTree{V, E}, source::V, target::V, edge::E) where {V, E}
8789
@assert target vertices(tree)
8890
add_edge!(tree.graph, source, target, edge)
@@ -96,6 +98,9 @@ function add_edge!(tree::SpanningTree{V, E}, source::V, target::V, edge::E) wher
9698
tree
9799
end
98100

101+
"""
102+
Replace an edge in both the tree and the underlying graph.
103+
"""
99104
function replace_edge!(tree::SpanningTree{V, E}, old_edge::E, new_edge::E) where {V, E}
100105
@assert old_edge edges(tree)
101106
src = source(old_edge, tree)
@@ -145,3 +150,21 @@ function lowest_common_ancestor(v1::V, v2::V, tree::SpanningTree{V, E}) where {V
145150
end
146151
v1
147152
end
153+
154+
"""
155+
Return a list of vertices in the subtree rooted at `subtree_root`, including `subtree_root` itself.
156+
The list is guaranteed to be topologically sorted.
157+
"""
158+
function subtree_vertices(subtree_root::V, tree::SpanningTree{V, E}) where {V, E}
159+
@assert subtree_root vertices(tree)
160+
frontier = [subtree_root]
161+
subtree_vertices = V[]
162+
while !isempty(frontier)
163+
parent = pop!(frontier)
164+
push!(subtree_vertices, parent)
165+
for child in out_neighbors(parent, tree)
166+
push!(frontier, child)
167+
end
168+
end
169+
return subtree_vertices
170+
end

src/mechanism_modification.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,41 @@ function submechanism(mechanism::Mechanism{T}, submechanismroot::RigidBody{T};
152152
ret
153153
end
154154

155+
"""
156+
Remove all bodies in the subtree rooted at `subtree_root`, including `subtree_root` itself,
157+
as well as all joints connected to these bodies.
158+
159+
The ordering of the joints that remain in the mechanism is retained.
160+
"""
161+
function remove_subtree!(mechanism::Mechanism{T}, subtree_root::RigidBody{T}) where {T}
162+
@assert subtree_root != root_body(mechanism)
163+
tree = mechanism.tree
164+
graph = mechanism.graph
165+
bodies_to_remove = subtree_vertices(subtree_root, tree)
166+
new_tree_joints = copy(edges(tree))
167+
for body in bodies_to_remove
168+
# Remove the tree joint from our new ordered list of joints.
169+
tree_joint = edge_to_parent(body, tree)
170+
deleteat!(new_tree_joints, findfirst(isequal(tree_joint), new_tree_joints))
171+
end
172+
for body in bodies_to_remove
173+
# Remove all edges to and from the vertex in the graph.
174+
for joint in copy(in_edges(body, graph))
175+
remove_edge!(graph, joint)
176+
end
177+
for joint in copy(out_edges(body, graph))
178+
remove_edge!(graph, joint)
179+
end
180+
181+
# Remove the vertex itself.
182+
remove_vertex!(graph, body)
183+
end
184+
# Construct a new spanning tree with the new list of tree joints.
185+
mechanism.tree = SpanningTree(graph, root_body(mechanism), new_tree_joints)
186+
canonicalize_graph!(mechanism)
187+
mechanism
188+
end
189+
155190
"""
156191
$(SIGNATURES)
157192

src/spatial/util.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ function rotation_vector_rate(rotation_vector::AbstractVector{T}, angular_veloci
9191
ω = angular_velocity_in_body
9292
@boundscheck length(ϕ) == 3 || error("ϕ has wrong length")
9393
@boundscheck length(ω) == 3 || error("ω has wrong length")
94+
ϕ̇ = ω +× ω) / 2
9495
θ = norm(ϕ)
95-
ϕ̇ = ω
96-
if θ > eps(θ)
96+
if θ > eps(typeof(θ))
9797
s, c = sincos(θ)
98-
ϕ̇ += × ω) / 2 + 1 / θ^2 * (1 -* s) / (2 * (1 - c))) * ϕ ×× ω)
98+
ϕ̇ += 1 / θ^2 * (1 -* s) / (2 * (1 - c))) * ϕ ×× ω)
9999
end
100100
ϕ̇
101101
end

test/test_graph.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,25 @@ Graphs.flip_direction(edge::Edge{Int32}) = Edge(-edge.data)
326326
end
327327
end
328328

329+
@testset "subtree_vertices" begin
330+
graph = DirectedGraph{Vertex{Int64}, Edge{Int32}}()
331+
root = Vertex(0)
332+
add_vertex!(graph, root)
333+
tree = SpanningTree(graph, root)
334+
for i = 1 : 30
335+
parent = vertices(tree)[rand(1 : num_vertices(graph))]
336+
child = Vertex(i)
337+
edge = Edge(Int32(i + 3))
338+
add_edge!(tree, parent, child, edge)
339+
end
340+
for subtree_root in vertices(tree)
341+
subtree = subtree_vertices(subtree_root, tree)
342+
for vertex in vertices(tree)
343+
@test (vertex subtree) == (subtree_root ancestors(vertex, tree))
344+
end
345+
end
346+
end
347+
329348
@testset "reindex!" begin
330349
Random.seed!(15)
331350
graph = DirectedGraph{Vertex{Int64}, Edge{Float64}}()

test/test_mechanism_modification.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,4 +357,55 @@
357357
showerror(devnull, e)
358358
end
359359
end
360+
361+
@testset "remove_subtree! - tree mechanism" begin
362+
mechanism = parse_urdf(joinpath(@__DIR__, "urdf", "atlas.urdf"), floating=true)
363+
@test_throws AssertionError remove_subtree!(mechanism, root_body(mechanism))
364+
365+
original_joints = copy(joints(mechanism))
366+
367+
# Behead.
368+
num_bodies = length(bodies(mechanism))
369+
num_joints = length(joints(mechanism))
370+
head = findbody(mechanism, "head")
371+
neck_joint = joint_to_parent(head, mechanism)
372+
remove_subtree!(mechanism, head)
373+
@test length(bodies(mechanism)) == num_bodies - 1
374+
@test length(joints(mechanism)) == num_joints - 1
375+
@test head bodies(mechanism)
376+
@test neck_joint joints(mechanism)
377+
378+
# Lop off an arm.
379+
num_bodies = length(bodies(mechanism))
380+
num_joints = length(joints(mechanism))
381+
r_clav = findbody(mechanism, "r_clav")
382+
r_hand = findbody(mechanism, "r_hand")
383+
r_arm = path(mechanism, r_clav, r_hand)
384+
arm_joints = collect(r_arm)
385+
arm_bodies = [r_clav; map(joint -> successor(joint, mechanism), arm_joints)]
386+
remove_subtree!(mechanism, r_clav)
387+
@test length(joints(mechanism)) == num_joints - length(arm_joints) - 1
388+
@test length(bodies(mechanism)) == num_bodies - length(arm_bodies)
389+
@test isempty(intersect(arm_joints, joints(mechanism)))
390+
@test isempty(intersect(arm_bodies, bodies(mechanism)))
391+
@test issorted(joints(mechanism), by=joint ->findfirst(isequal(joint), original_joints))
392+
end
393+
394+
@testset "remove_subtree! - maximal coordinates" begin
395+
original = parse_urdf(joinpath(@__DIR__, "urdf", "atlas.urdf"), floating=true)
396+
mechanism = maximal_coordinates(original)
397+
num_bodies = length(bodies(mechanism))
398+
num_joints = length(joints(mechanism))
399+
@test_throws AssertionError remove_subtree!(mechanism, findbody(original, "head")) # body not in tree
400+
head = findbody(mechanism, "head")
401+
head_joints = copy(in_joints(head, mechanism))
402+
@test length(head_joints) == 2 # floating joint + neck loop joint
403+
remove_subtree!(mechanism, head)
404+
@test length(joints(mechanism)) == num_joints - length(head_joints)
405+
@test length(bodies(mechanism)) == num_bodies - 1
406+
for joint in head_joints
407+
@test joint joints(mechanism)
408+
end
409+
@test head bodies(mechanism)
410+
end
360411
end # mechanism modification

test/test_simulate.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,28 @@
225225
energy20 = gravitational_potential_energy(state) + kinetic_energy(state)
226226
@test energy20 energy15 atol=1e-5 # stabilization doesn't significantly affect energy after converging
227227
end
228+
229+
@testset "Ball joint pendulum" begin
230+
# Issue #617
231+
translation = [0.3, 0, 0]
232+
233+
world = RigidBody{Float64}("world")
234+
pendulum = Mechanism(world; gravity=[0., 0., -9.81])
235+
236+
center_of_mass = [0, 0, 0.2]
237+
q0 = [cos(pi/8), sin(pi/8), 0.0, 0.0]
238+
239+
joint1 = Joint("joint1", QuaternionSpherical{Float64}())
240+
inertia1 = SpatialInertia(frame_after(joint1), com=center_of_mass, moment_about_com=diagm([1.,1.,1.]), mass=1.)
241+
link1 = RigidBody("link1", inertia1)
242+
before_joint1_to_world = Transform3D(frame_before(joint1), default_frame(world), one(RotMatrix{3}), SVector{3}(translation))
243+
attach!(pendulum, world, link1, joint1, joint_pose=before_joint1_to_world)
244+
245+
state = MechanismState(pendulum)
246+
247+
set_configuration!(state, joint1, q0)
248+
ts, qs, vs = simulate(state, 1., Δt=0.001)
249+
@test all(all(!isnan, q) for q in qs)
250+
@test all(all(!isnan, v) for v in vs)
251+
end
228252
end

0 commit comments

Comments
 (0)