00001
00002
00003 import rospy
00004 import numpy
00005 from nav_msgs.msg import Odometry
00006 from geometry_msgs.msg import Point, Quaternion, Twist, Vector3, TwistWithCovariance, TransformStamped
00007 from std_msgs.msg import Float64, Empty
00008 import tf
00009 import time
00010 import threading
00011 import copy
00012 from dynamic_reconfigure.server import Server
00013 from jsk_robot_startup.cfg import OdometryOffsetReconfigureConfig
00014 from odometry_utils import make_homogeneous_matrix, update_twist_covariance, update_pose, update_pose_covariance, broadcast_transform, transform_local_twist_to_global, transform_local_twist_covariance_to_global
00015
00016 class OdometryOffset(object):
00017 def __init__(self):
00018 rospy.init_node("OdometryOffset", anonymous=True)
00019 self.offset_matrix = None
00020 self.prev_odom = None
00021 self.initial_base_link_transform = make_homogeneous_matrix([0, 0, 0], [0, 0, 0, 1])
00022 self.lock = threading.Lock()
00023
00024 self.rate = float(rospy.get_param("~rate", 100))
00025 self.r = rospy.Rate(self.rate)
00026
00027 self.publish_tf = rospy.get_param("~publish_tf", True)
00028 if self.publish_tf:
00029 self.broadcast = tf.TransformBroadcaster()
00030 self.invert_tf = rospy.get_param("~invert_tf", True)
00031 self.odom_frame = rospy.get_param("~odom_frame", "offset_odom")
00032 self.base_link_frame = rospy.get_param("~base_link_frame", "BODY")
00033 self.tf_duration = rospy.get_param("~tf_duration", 1)
00034
00035 self.use_twist_filter = rospy.get_param("~use_twist_filter", False)
00036 if self.use_twist_filter:
00037 self.filter_buffer_size = rospy.get_param("~filter_buffer_size", 5)
00038 self.filter_buffer = []
00039
00040 self.overwrite_pdf = rospy.get_param("~overwrite_pdf", False)
00041 if self.overwrite_pdf:
00042 self.twist_proportional_sigma = rospy.get_param("~twist_proportional_sigma", False)
00043 self.v_err_mean = [rospy.get_param("~mean_x", 0.0),
00044 rospy.get_param("~mean_y", 0.0),
00045 rospy.get_param("~mean_z", 0.0),
00046 rospy.get_param("~mean_roll", 0.0),
00047 rospy.get_param("~mean_pitch", 0.0),
00048 rospy.get_param("~mean_yaw", 0.0)]
00049 self.v_err_sigma = [rospy.get_param("~sigma_x", 0.05),
00050 rospy.get_param("~sigma_y", 0.1),
00051 rospy.get_param("~sigma_z", 0.0001),
00052 rospy.get_param("~sigma_roll", 0.0001),
00053 rospy.get_param("~sigma_pitch", 0.0001),
00054 rospy.get_param("~sigma_yaw", 0.01)]
00055 self.reconfigure_server = Server(OdometryOffsetReconfigureConfig, self.reconfigure_callback)
00056 self.source_odom_sub = rospy.Subscriber("~source_odom", Odometry, self.source_odom_callback)
00057 self.init_transform_sub = rospy.Subscriber("~initial_base_link_transform", TransformStamped, self.init_transform_callback)
00058 self.pub = rospy.Publisher("~output", Odometry, queue_size = 1)
00059
00060 def reconfigure_callback(self, config, level):
00061 with self.lock:
00062 for i, mean in enumerate(["mean_x", "mean_y", "mean_z", "mean_roll", "mean_pitch", "mean_yaw"]):
00063 self.v_err_mean[i] = config[mean]
00064 for i, sigma in enumerate(["sigma_x", "sigma_y", "sigma_z", "sigma_roll", "sigma_pitch", "sigma_yaw"]):
00065 self.v_err_sigma[i] = config[sigma]
00066 rospy.loginfo("[%s]" + "velocity mean updated: x: {0}, y: {1}, z: {2}, roll: {3}, pitch: {4}, yaw: {5}".format(*self.v_err_mean), rospy.get_name())
00067 rospy.loginfo("[%s]" + "velocity sigma updated: x: {0}, y: {1}, z: {2}, roll: {3}, pitch: {4}, yaw: {5}".format(*self.v_err_sigma), rospy.get_name())
00068 return config
00069
00070 def calculate_offset(self, source_odom):
00071
00072
00073
00074 if self.initial_base_link_transform == None:
00075 rospy.loginfo("[%s] initial transform is not subscribed yet", rospy.get_name())
00076 return None
00077 source_odom_matrix = make_homogeneous_matrix([getattr(source_odom.pose.pose.position, attr) for attr in ["x", "y", "z"]], [getattr(source_odom.pose.pose.orientation, attr) for attr in ["x", "y", "z", "w"]])
00078 return self.initial_base_link_transform.dot(numpy.linalg.inv(source_odom_matrix))
00079
00080 def execute(self):
00081 while not rospy.is_shutdown():
00082 self.r.sleep()
00083
00084 def init_transform_callback(self, msg):
00085 with self.lock:
00086 self.initial_base_link_transform = make_homogeneous_matrix([getattr(msg.transform.translation, attr) for attr in ["x", "y", "z"]], [getattr(msg.transform.rotation, attr) for attr in ["x", "y", "z", "w"]])
00087 self.prev_odom = None
00088 self.offset_matrix = None
00089 if self.use_twist_filter:
00090 self.filter_buffer = []
00091
00092 def source_odom_callback(self, msg):
00093 with self.lock:
00094 if self.offset_matrix != None:
00095 source_odom_matrix = make_homogeneous_matrix([getattr(msg.pose.pose.position, attr) for attr in ["x", "y", "z"]], [getattr(msg.pose.pose.orientation, attr) for attr in ["x", "y", "z", "w"]])
00096 new_odom = copy.deepcopy(msg)
00097 new_odom.header.frame_id = self.odom_frame
00098 new_odom.child_frame_id = self.base_link_frame
00099 twist_list = [new_odom.twist.twist.linear.x, new_odom.twist.twist.linear.y, new_odom.twist.twist.linear.z, new_odom.twist.twist.angular.x, new_odom.twist.twist.angular.y, new_odom.twist.twist.angular.z]
00100
00101
00102 if self.use_twist_filter:
00103 twist_list = self.median_filter(twist_list)
00104 new_odom.twist.twist = Twist(Vector3(*twist_list[0:3]), Vector3(*twist_list[3: 6]))
00105
00106
00107 if self.overwrite_pdf:
00108 if not all([abs(x) < 1e-3 for x in twist_list]):
00109
00110 twist_list = [x + y for x, y in zip(twist_list, self.v_err_mean)]
00111 new_odom.twist.twist = Twist(Vector3(*twist_list[0:3]), Vector3(*twist_list[3: 6]))
00112
00113 if self.twist_proportional_sigma:
00114 current_sigma = [x * y for x, y in zip(twist_list, self.v_err_sigma)]
00115 else:
00116 if all([abs(x) < 1e-3 for x in twist_list]):
00117 current_sigma = [1e-6] * 6
00118 else:
00119 current_sigma = self.v_err_sigma
00120 new_odom.twist.covariance = update_twist_covariance(new_odom.twist, current_sigma)
00121
00122
00123 new_odom_matrix = self.offset_matrix.dot(source_odom_matrix)
00124 new_odom.pose.pose.position = Point(*list(new_odom_matrix[:3, 3]))
00125 new_odom.pose.pose.orientation = Quaternion(*list(tf.transformations.quaternion_from_matrix(new_odom_matrix)))
00126
00127 if self.overwrite_pdf:
00128 if self.prev_odom != None:
00129 dt = (new_odom.header.stamp - self.prev_odom.header.stamp).to_sec()
00130 global_twist_with_covariance = TwistWithCovariance(transform_local_twist_to_global(new_odom.pose.pose, new_odom.twist.twist),
00131 transform_local_twist_covariance_to_global(new_odom.pose.pose, new_odom.twist.covariance))
00132 new_odom.pose.covariance = update_pose_covariance(self.prev_odom.pose.covariance, global_twist_with_covariance.covariance, dt)
00133 else:
00134 new_odom.pose.covariance = numpy.diag([0.01**2] * 6).reshape(-1,).tolist()
00135 else:
00136
00137 new_pose_cov_matrix = numpy.matrix(new_odom.pose.covariance).reshape(6, 6)
00138 rotation_matrix = self.offset_matrix[:3, :3]
00139 new_pose_cov_matrix[:3, :3] = (rotation_matrix.T).dot(new_pose_cov_matrix[:3, :3].dot(rotation_matrix))
00140 new_pose_cov_matrix[3:6, 3:6] = (rotation_matrix.T).dot(new_pose_cov_matrix[3:6, 3:6].dot(rotation_matrix))
00141 new_odom.pose.covariance = numpy.array(new_pose_cov_matrix).reshape(-1,).tolist()
00142
00143
00144 self.pub.publish(new_odom)
00145 if self.publish_tf:
00146 broadcast_transform(self.broadcast, new_odom, self.invert_tf)
00147 self.prev_odom = new_odom
00148
00149 else:
00150 self.offset_matrix = self.calculate_offset(msg)
00151
00152 def median_filter(self, data):
00153 self.filter_buffer.append(data)
00154 ret = numpy.median(self.filter_buffer, axis = 0)
00155 if len(self.filter_buffer) >= self.filter_buffer_size:
00156 self.filter_buffer.pop(0)
00157 return ret