Skip to content

Commit 1792315

Browse files
Merge pull request #11 from Michi-Tsubaki/add-tf2
Add tf2
2 parents 0e4beb7 + 11bc08b commit 1792315

File tree

3 files changed

+209
-2
lines changed

3 files changed

+209
-2
lines changed

examples/example_tf2.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env julia
2+
# This example demonstrates minimal RViz integration with TF and visualization markers.
3+
# In other terminal(bash), run: `ros2 run rviz2 rviz2`
4+
5+
using ROS2
6+
7+
init()
8+
node = ROSNode("rviz_minimal")
9+
10+
static_broadcaster = StaticTransformBroadcaster(node)
11+
broadcaster = TransformBroadcaster(node)
12+
listener = TransformListener(node)
13+
pose_pub = Publisher(node, "robot_pose", "geometry_msgs.msg.PoseStamped")
14+
marker_pub = Publisher(node, "visualization_marker", "visualization_msgs.msg.Marker")
15+
16+
static_transform =
17+
create_transform_stamped("map", "odom", (0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0))
18+
static_transform.header.stamp = to_msg_time(now())
19+
send_static_transform(static_broadcaster, static_transform)
20+
21+
robot_x = Ref(0.0)
22+
robot_y = Ref(0.0)
23+
robot_theta = Ref(0.0)
24+
25+
timer = ROSTimer(
26+
node,
27+
0.1,
28+
() -> begin
29+
robot_theta[] += 0.1
30+
robot_x[] = 2.0 * cos(robot_theta[])
31+
robot_y[] = 2.0 * sin(robot_theta[])
32+
33+
qz = sin(robot_theta[] / 2.0)
34+
qw = cos(robot_theta[] / 2.0)
35+
36+
odom_transform = create_transform_stamped(
37+
"odom",
38+
"base_link",
39+
(robot_x[], robot_y[], 0.0),
40+
(0.0, 0.0, qz, qw),
41+
)
42+
odom_transform.header.stamp = to_msg_time(now())
43+
send_transform(broadcaster, odom_transform)
44+
45+
pose_msg = create_msg("geometry_msgs.msg.PoseStamped")
46+
pose_msg.header.stamp = to_msg_time(now())
47+
pose_msg.header.frame_id = "map"
48+
pose_msg.pose.position.x = robot_x[]
49+
pose_msg.pose.position.y = robot_y[]
50+
pose_msg.pose.position.z = 0.0
51+
pose_msg.pose.orientation.x = 0.0
52+
pose_msg.pose.orientation.y = 0.0
53+
pose_msg.pose.orientation.z = qz
54+
pose_msg.pose.orientation.w = qw
55+
publish(pose_pub, pose_msg)
56+
57+
marker_msg = create_msg("visualization_msgs.msg.Marker")
58+
marker_msg.header.stamp = to_msg_time(now())
59+
marker_msg.header.frame_id = "base_link"
60+
marker_msg.type = 2
61+
marker_msg.action = 0
62+
marker_msg.pose.position.x = 0.0
63+
marker_msg.pose.position.y = 0.0
64+
marker_msg.pose.position.z = 0.5
65+
marker_msg.pose.orientation.w = 1.0
66+
marker_msg.scale.x = 0.5
67+
marker_msg.scale.y = 0.5
68+
marker_msg.scale.z = 1.0
69+
marker_msg.color.a = 1.0
70+
marker_msg.color.r = 1.0
71+
marker_msg.color.g = 0.0
72+
marker_msg.color.b = 0.0
73+
marker_msg.id = 0
74+
publish(marker_pub, marker_msg)
75+
76+
try
77+
transform = lookup_transform(listener, "map", "base_link")
78+
println(
79+
"Robot at: x=$(round(transform.transform.translation.x, digits=2)), y=$(round(transform.transform.translation.y, digits=2))",
80+
)
81+
catch
82+
end
83+
end,
84+
)
85+
86+
try
87+
while true
88+
spin_once(node)
89+
sleep(0.05)
90+
end
91+
finally
92+
timer_cancel(timer)
93+
shutdown()
94+
end

src/ROS2.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ include("service.jl")
1212
include("parameter.jl")
1313
include("action.jl")
1414
include("logging.jl")
15+
include("tf2.jl")
1516

1617
function __init__()
1718
if contains(lowercase(get(ENV, "GITHUB_WORKFLOW", "")), "automerge")
@@ -38,6 +39,7 @@ function __init__()
3839
copy!(rclpy_node, pyimport("rclpy.node"))
3940

4041
Time.__init_time__()
42+
TF2.__init_tf2__()
4143

4244
catch e
4345
@warn "ROS2 initialization deferred: $e"
@@ -52,6 +54,7 @@ using .Service
5254
using .Parameter
5355
using .Action
5456
using .Logging
57+
using .TF2
5558

5659
export ROSNode,
5760
init,
@@ -113,11 +116,19 @@ export ROSNode,
113116
warn,
114117
log_error,
115118
fatal,
116-
set_level, # from Logging
119+
set_level,
117120
DEBUG,
118121
INFO,
119122
WARN,
120123
ERROR,
121-
FATAL
124+
FATAL,
125+
TransformListener,
126+
TransformBroadcaster,
127+
StaticTransformBroadcaster,
128+
lookup_transform,
129+
can_transform,
130+
send_transform,
131+
send_static_transform,
132+
create_transform_stamped # from TF2
122133

123134
end

src/tf2.jl

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
module TF2
2+
3+
using PyCall
4+
using ..Core: ROSNode
5+
6+
const tf2_ros = PyNULL()
7+
const tf2_geometry_msgs = PyNULL()
8+
const geometry_msgs = PyNULL()
9+
10+
function __init_tf2__()
11+
copy!(tf2_ros, pyimport("tf2_ros"))
12+
copy!(tf2_geometry_msgs, pyimport("tf2_geometry_msgs"))
13+
copy!(geometry_msgs, pyimport("geometry_msgs.msg"))
14+
end
15+
16+
mutable struct TransformListener
17+
_py_listener::PyObject
18+
_buffer::PyObject
19+
20+
function TransformListener(node::ROSNode)
21+
buffer = tf2_ros.Buffer()
22+
listener = tf2_ros.TransformListener(buffer, node.pynode)
23+
new(listener, buffer)
24+
end
25+
end
26+
27+
mutable struct TransformBroadcaster
28+
_py_broadcaster::PyObject
29+
30+
function TransformBroadcaster(node::ROSNode)
31+
broadcaster = tf2_ros.TransformBroadcaster(node.pynode)
32+
new(broadcaster)
33+
end
34+
end
35+
36+
mutable struct StaticTransformBroadcaster
37+
_py_broadcaster::PyObject
38+
39+
function StaticTransformBroadcaster(node::ROSNode)
40+
broadcaster = tf2_ros.StaticTransformBroadcaster(node.pynode)
41+
new(broadcaster)
42+
end
43+
end
44+
45+
function lookup_transform(
46+
listener::TransformListener,
47+
target_frame::String,
48+
source_frame::String,
49+
time = nothing,
50+
)
51+
if time === nothing
52+
return listener._buffer.lookup_transform(target_frame, source_frame, tf2_ros.Time())
53+
else
54+
return listener._buffer.lookup_transform(target_frame, source_frame, time)
55+
end
56+
end
57+
58+
function can_transform(
59+
listener::TransformListener,
60+
target_frame::String,
61+
source_frame::String,
62+
time = nothing,
63+
)
64+
if time === nothing
65+
return listener._buffer.can_transform(target_frame, source_frame, tf2_ros.Time())
66+
else
67+
return listener._buffer.can_transform(target_frame, source_frame, time)
68+
end
69+
end
70+
71+
function send_transform(broadcaster::TransformBroadcaster, transform)
72+
broadcaster._py_broadcaster.sendTransform(transform)
73+
end
74+
75+
function send_static_transform(broadcaster::StaticTransformBroadcaster, transform)
76+
broadcaster._py_broadcaster.sendTransform(transform)
77+
end
78+
79+
function create_transform_stamped(
80+
parent_frame::String,
81+
child_frame::String,
82+
translation::Tuple{Float64,Float64,Float64},
83+
rotation::Tuple{Float64,Float64,Float64,Float64},
84+
)
85+
transform = geometry_msgs.TransformStamped()
86+
transform.header.frame_id = parent_frame
87+
transform.child_frame_id = child_frame
88+
transform.transform.translation.x = translation[1]
89+
transform.transform.translation.y = translation[2]
90+
transform.transform.translation.z = translation[3]
91+
transform.transform.rotation.x = rotation[1]
92+
transform.transform.rotation.y = rotation[2]
93+
transform.transform.rotation.z = rotation[3]
94+
transform.transform.rotation.w = rotation[4]
95+
return transform
96+
end
97+
98+
export TransformListener, TransformBroadcaster, StaticTransformBroadcaster
99+
export lookup_transform, can_transform, send_transform, send_static_transform
100+
export create_transform_stamped
101+
102+
end

0 commit comments

Comments
 (0)