My broadcast script, which performs the transformation between the robot base (base_footprint) and the odometry frame(odom), emulating what Gazebo broadcasts (but now for a real robot) were wrong. I was sending fixed data instead of real values data of imu and odom. The code of this broadcaster is below if someone needs to do something similar.
2- Used message_filters pkg in this broadcaster to synchronize the timestamp of odom and imu data
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import TransformStamped
from tf2_ros import TransformBroadcaster
import math
from sensor_msgs.msg import Imu
from nav_msgs.msg import Odometry
#import tf_transformations
import transforms3d.euler
from message_filters import ApproximateTimeSynchronizer, Subscriber
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy
class DynamicTransformBroadcaster(Node):
def __init__(self):
super().__init__('tf2_dynamic_broadcaster')
self.broadcaster = TransformBroadcaster(self)
#Timer not requeired when using message_fitlers synch time.
#self.timer = self.create_timer(0.1, self.broadcast_transforms)
self._imu_data = None
self._odom_data = None
qos_profile = QoSProfile(
reliability=QoSReliabilityPolicy.BEST_EFFORT,
history=QoSHistoryPolicy.KEEP_LAST,
depth=100
)
imu_sub = Subscriber(self, Imu, 'imu', qos_profile = qos_profile)
odom_sub = Subscriber(self, Odometry, 'odom', qos_profile = qos_profile)
self.ts = ApproximateTimeSynchronizer([imu_sub, odom_sub], queue_size=100, slop=0.2)
self.ts.registerCallback(self.callback)
def callback(self, imu_msg, odom_msg):
self._imu_data = imu_msg.orientation
self._odom_data = odom_msg
self.broadcast_transforms()
def get_yaw_from_quaternion(self, q):
#euler = tf_transformations.euler_from_quaternion([q.x, q.y, q.z, q.w])
euler = transforms3d.euler.quat2euler([q.w, q.x, q.y, q.z])
return euler[2] # yaw
def broadcast_transforms(self):
now = self.get_clock().now().to_msg()
if self._imu_data is None or self._odom_data is None:
self.get_logger().info("Waiting for IMU and Odometry data...")
return
# Transform from odom to base_footprint
odom_to_base_footprint = TransformStamped()
odom_to_base_footprint.header.stamp = now
odom_to_base_footprint.header.frame_id = 'odom'
odom_to_base_footprint.child_frame_id = 'base_footprint'
odom_to_base_footprint.transform.translation.x = self._odom_data.pose.pose.position.x
odom_to_base_footprint.transform.translation.y = self._odom_data.pose.pose.position.y
odom_to_base_footprint.transform.translation.z = 0.0
yaw = self.get_yaw_from_quaternion(self._imu_data) # math.radians(0)
odom_to_base_footprint.transform.rotation.x = 0.0
odom_to_base_footprint.transform.rotation.y = 0.0
odom_to_base_footprint.transform.rotation.z = math.sin(yaw / 2)
odom_to_base_footprint.transform.rotation.w = math.cos(yaw / 2)
# Broadcasting the transforms
self.broadcaster.sendTransform(odom_to_base_footprint)
def main(args=None):
rclpy.init(args=args)
node = DynamicTransformBroadcaster()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
rclpy.shutdown()
if __name__ == '__main__':
main()
3- Increased the queue_size on the navsat params file from 10 to 100. This avoided jumps.
# For parameter descriptions, please refer to the template parameter files for each node.
ekf_filter_node_odom:
ros__parameters:
frequency: 30.0
two_d_mode: true # Recommended to use 2d mode for nav2 in mostly planar environments
publish_acceleration: false
#print_diagnostics: true
debug: false
publish_tf: true
# 1. Set the map_frame, odom_frame, and base_link frames to the appropriate frame names for your system.
# 1a. If your system does not have a map_frame, just remove it, and make sure "world_frame" is set to the value of odom_frame.
# 2. If you are fusing continuous position data such as wheel encoder odometry, visual odometry, or IMU data, set "world_frame"
# to your odom_frame value. This is the default behavior for robot_localization's state estimation nodes.
# 3. If you are fusing global absolute position data that is subject to discrete jumps (e.g., GPS or position updates from landmark
# observations) then:
# 3a. Set your "world_frame" to your map_frame value
# 3b. MAKE SURE something else is generating the odom->base_link transform. Note that this can even be another state estimation node
#
map_frame: map
odom_frame: odom
base_link_frame: base_footprint # the frame id used by the turtlebot's diff drive plugin
world_frame: map #odom # it seemst that for GPS this is how must be used : https://robotics.stackexchange.com/questions/101504/robot-localization-not-publishing-odom-base-link
odom0: /odom
odom0_config: [false, false, false, # Position X, Y, Z
false, false, false, # Orientation roll, pitch, yaw (only yaw is used)
true, false, false, # Velocity X dot, Y dot, Z dot
false, false, false, # Angular Velocity roll dot, pitch dot, yaw dot
false, false, false] # Acceleration X double dot, Y double dot, Z double dot
odom0_differential: false # Typically false for odometry, as it's usually more accurate.
odom0_nodelay: true # Ignore delays in the odometry data.
odom0_relative: false
odom0_queue_size: 100
imu0: /imu
imu0_config: [false, false, false, # Position X, Y, Z
false, false, true, # Orientation roll, pitch, yaw (only yaw is used)
false, false, false, # Velocity X dot, Y dot, Z dot
false, false, true, # Angular Velocity roll dot, pitch dot, yaw dot
false, false, false] # Acceleration X double dot, Y double dot, Z double dot
imu0_differential: true # Set to true for real robot IMU data due to typical inaccuracies.
# If using a real robot you might want to set this to true, since usually absolute measurements from real imu's are not very accurate
imu0_nodelay: true # Ignore delays in the imu data.
imu0_relative: false
imu0_queue_size: 100
imu0_remove_gravitational_acceleration: true
use_control: false
process_noise_covariance: [1e-3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1e-3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1e-3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1]
ekf_filter_node_map:
ros__parameters:
use_sim_time: false
sensor_timeout: 0.25 # Typical value: 0.1-0.5 seconds. Tolerance for sensor synchronization.
transform_time_offset: 0.0 # Typical value: 0.0 seconds. Time offset for transforms.
transform_timeout: 0.25 # Typical value: 0.1-0.5 seconds. Time to wait for a transform.
freq
uency: 30.0
two_d_mode: false #true # Recommended to use 2d mode for nav2 in mostly planar environments
#print_diagnostics: true
publish_acceleration: false
debug: false
publish_tf: true
reset_on_time_jump: false
map_frame: map
odom_frame: odom
base_link_frame: base_footprint # the frame id used by the turtlebot's diff drive plugin
world_frame: map
odom0: /odom #/odometry/local # = "fused odometry + Imu in previous node"
odom0_config: [true, true, false, # Position X, Y, Z
false, false, false, # Orientation roll, pitch, yaw (only yaw is used)
true, false, false, # Velocity X dot, Y dot, Z dot
false, false, false, # Angular Velocity roll dot, pitch dot, yaw dot
false, false, false] # Acceleration X double dot, Y double dot, Z double dot
odom0_queue_size: 100
odom0_nodelay: true
odom0_differential: false # Typically false for odometry, as it's usually more accurate.
odom0_relative: false
imu0: /imu
imu0_config: [false, false, false, # Position X, Y, Z
false, false, true, # Orientation roll, pitch, yaw (only yaw is used)
false, false, false, # Velocity X dot, Y dot, Z dot
false, false, false, # Angular Velocity roll dot, pitch dot, yaw dot
false, false, false] # Acceleration X double dot, Y double dot, Z double dot
imu0_differential: true # Set to true for real robot IMU data due to typical inaccuracies.
imu0_nodelay: false # Ignore delays in the imu data.
imu0_relative: false
imu0_queue_size: 100
imu0_remove_gravitational_acceleration: true
#O problema esta aqui, o gazebo publica gps namespace, porém ele deve poder englobar diferentes tipos de dados (Odometry) e também receber o (NavSatFix). Já quando eu uso real_gps/fix aqui
#Eu envio apeans NavSatFix, mas não tenho um namespace para receber outros tipos de mensagens que vem da Fusão anterior (odometry/local) que o ekf_filter_node_odom está entregaando.
odom1: odometry/gps # try gps if not fix
odom1_config: [true, true, false, # Position X, Y, Z
false, false, false, # Orientation roll, pitch, yaw (only yaw is used)
false, false, false, # Velocity X dot, Y dot, Z dot
false, false, false, # Angular Velocity roll dot, pitch dot, yaw dot
false, false, false] # Acceleration X double dot, Y double dot, Z double dot
odom1_queue_size: 100
odom1_nodelay: true # Ignore delays in the odometry/gps data.
odom1_differential: false # Typically false for odometry, as it's usually more accurate.
odom1_relative: false
use_control: false
process_noise_covariance: [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1e-3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1]
navsat_transform:
ros__parameters:
use_sim_time: false
frequency: 30.0
delay: 3.0 # Typical value: 0.0-3.0 seconds. Accounts for delay in the sensor data. GPS usually has inherent delays
magnetic_declination_radians: 0.266512 #0.0
yaw_offset: 0.0
zero_altitude: true
broadcast_utm_transform: true
publish_filtered_gps: true
use_odometry_yaw: false #true
wait_for_datum: true #true
datum: [-23.659684066666667, -46.59128556666667, 0.0] # pre-set datum if needed, [lat, lon, yaw]
However as you can check in the video below, I still have some jumping on the beginning of localization (I guess may be related to GPS wrong synchronization with imu and odom, but not sure), and I still didn't figure out how to fix it. If someone knows please let me know.
Fixed Dynamic Broadcaster
I consider the issue is 99% fixed.
Initial Pose Jump
-----------------UPDATE-------------------------
Fixed increasing both covariance matrixes diagonals values for 10 times each value!
No initial Jump