Publishing Odometry in ROS2 - orientation is not reflected in RViz
Hi!
I'm trying to publish odometry messages based on encoder values coming from motors.
I'm using ROS2 (Eloquent).
Odometry messages are published, but the orientation fo the robot is not correct (the arrow is always pointing up in RViz)
Below are more details.
Motors are controlled by Arduino which uses Serial port.
I send desired velocities in mm/s (linear) and radians/s (angular).
Arduno sends back actual speeds for left and right wheels based on encoder data as comma-separated lines:
duration_ms,left_velocity_mm_s,right_velocity_mm_s
After calculating linear and angular velocities they are very similar to the desired ones.
For example, when I send 0.08
m/s linear and 0.5
r/s angular vx
and vth
are something like 0.086
and 0.023091
when going straingt and
left: 0.041, right: 0.119 vth: 0.439
when turning left.
The problem is that after launching RViz2 and adding Odometry
display I can see that the position of the robot is changing and it follows my commands, but
the direction is always the same - it's always pointing up.
It looks like the robot is moving sideways which it can't do :)
As far as I understand it should point in the direction in which robot is pointed.
What am I missing here?
Here is how it looks in RViz:
Below is the code, please note that I'm using quaternion_from_euler
from transformations
library https://pypi.org/project/transformati..., because I think it's not packaged with ros2_tf
.
odom_pub = node.create_publisher(
Odometry, '/odom', qos_profile=qos_profile_services_default)
odom_broadcaster = tf2_ros.TransformBroadcaster(node, qos_profile_services_default)
x = 0.0
y = 0.0
th = 0.0
WHEEL_BASE = 0.177
while True:
bytesToRead = ser.inWaiting()
if bytesToRead > 0:
line = str(ser.readline(), 'ascii')
parts = line.split(",")
dt = int(parts[0]) / 1000.0 # convert to seconds
v_left = float(parts[1]) / 1000.0 # convert to metres from mm
v_right = float(parts[2]) / 1000.0
vx = (v_right + v_left) / 2.0
vy = 0.0
vth = (v_right - v_left) / WHEEL_BASE
delta_x = (vx * cos(th) - vy * sin(th)) * dt
delta_y = (vx * sin(th) + vy * cos(th)) * dt
delta_th = vth * dt
x += delta_x
y += delta_y
th += delta_th
odom_quat = quaternion_from_euler(0, 0, th)
current_time = node.get_clock().now().to_msg()
t = TransformStamped()
t.header.stamp = current_time
t.header.frame_id = "odom"
t.child_frame_id = "base_link"
t.transform.translation.x = x
t.transform.translation.y = y
t.transform.translation.z = 0.0
t.transform.rotation.x = odom_quat[0]
t.transform.rotation.y = odom_quat[1]
t.transform.rotation.z = odom_quat[2]
t.transform.rotation.w = odom_quat[3]
odom_broadcaster.sendTransform(t)
odom = Odometry()
odom.header.frame_id = "odom"
odom.header.stamp = current_time
# set the position
odom.pose.pose.position.x = x
odom.pose.pose.position.y = y
odom.pose.pose.position.z = 0.0
odom.pose.pose.orientation.x = odom_quat[0]
odom.pose.pose.orientation.y = odom_quat[1]
odom.pose.pose.orientation.z = odom_quat[2]
odom.pose.pose.orientation.w = odom_quat[3]
# set the velocity
odom.child_frame_id = "base_link"
odom.twist.twist.linear.x = vx
odom.twist.twist.linear.y = vy
odom ...
without looking at anything else, this looks fishy.
If I put those values into this site, I only get
0
s, which would correspond to what you are describing.I'd check whether your calculations are correct.
Thanks for the hint! This made me look into quaternions which led to a "solution" ;)