ESP_diff_tf
def __init__(self):
rospy.init_node("diff_tf")
self.nodename = rospy.get_name()
rospy.loginfo("-I- %s started" % self.nodename)
#### parameters #######
self.rate = rospy.get_param('mr_robot_firmware/rate') # the rate at which to publish the transform
self.ticks_meter = float(rospy.get_param('mr_robot_firmware/ticks_meter')) # The number of wheel encoder ticks per meter of travel
self.base_width = float(rospy.get_param('mr_robot_firmware/base_width')) # The wheel base width in meters
self.base_frame_id = rospy.get_param('mr_robot_firmware/base_frame_id') # the name of the base frame of the robot
self.odom_frame_id = rospy.get_param('mr_robot_firmware/odom_frame_id') # the name of the odometry reference frame
self.encoder_min = rospy.get_param('encoder_min', -2147483648)
self.encoder_max = rospy.get_param('encoder_max', 2147483648)
self.encoder_low_wrap = rospy.get_param('wheel_low_wrap', (self.encoder_max - self.encoder_min) * 0.3 + self.encoder_min )
self.encoder_high_wrap = rospy.get_param('wheel_high_wrap', (self.encoder_max - self.encoder_min) * 0.7 + self.encoder_min )
self.t_delta = rospy.Duration(1.0/self.rate)
self.t_next = rospy.Time.now() + self.t_delta
# internal data
self.enc_left = None # wheel encoder readings
self.enc_right = None
self.left = 0 # actual values coming back from robot
self.right = 0
self.lmult = 0
self.rmult = 0
self.prev_lencoder = 0
self.prev_rencoder = 0
self.x = 0 # position in xy plane
self.y = 0
self.th = 0
self.dx = 0 # speeds in x/rotation
self.dr = 0
self.then = rospy.Time.now()
self.left_speed = 0
self.right_speed = 0
# subscriptions
rospy.Subscriber("left_encoder", Int32, self.lwheelCallback)
rospy.Subscriber("right_encoder", Int32, self.rwheelCallback)
rospy.Subscriber("initialpose", PoseWithCovarianceStamped, self.update_pose)
self.odomPub = rospy.Publisher("odom", Odometry,queue_size=10)
self.left_speed_pub = rospy.Publisher("left_speed", Float64,queue_size=10)
self.right_speed_pub = rospy.Publisher("right_speed", Float64,queue_size=10)
self.odomBroadcaster = TransformBroadcaster()
In this part we specify basic parameter like wheel encoder readings ,rate, base_width etc. As well subscribing & publishing encoder and odom values.
def update_pose(self, data):
(roll, pitch, yaw) = euler_from_quaternion([data.pose.pose.orientation.x, data.pose.pose.orientation.y, data.pose.pose.orientation.z, data.pose.pose.orientation.w])
self.x = data.pose.pose.position.x
self.y = data.pose.pose.position.y
self.th = yaw
Inside the update_pose()
method, the x, y, and z coordinates of the object’s position are extracted from the data dictionary and stored in the self.position attribute of the PoseEstimator object. This attribute is a tuple that contains the x, y, and z coordinates of the object’s position, respectively.
def update(self):
now = rospy.Time.now()
if now > self.t_next:
elapsed = now - self.then
self.then = now
elapsed = elapsed.to_sec()
# calculate odometry
if self.enc_left == None:
d_left = 0
d_right = 0
else:
d_left = (self.left - self.enc_left) / self.ticks_meter
d_right = (self.right - self.enc_right) / self.ticks_meter
# calculate the velocity of the 2 wheels
self.left_speed = self.left / elapsed
self.right_speed = self.right / elapsed
# print(d_left)
# print(self.right_speed)
self.left_speed_pub.publish(float(d_left))
self.right_speed_pub.publish(float(d_right))
self.enc_left = self.left
self.enc_right = self.right
# distance traveled is the average of the two wheels
d = ( d_left + d_right ) / 2
# this approximation works (in radians) for small angles
th = ( d_right - d_left ) / self.base_width
# calculate velocities
self.dx = d / elapsed
self.dr = th / elapsed
This code segment is checking whether the current time now is greater than the next time to update the pose t_next. If the condition is True, it proceeds to calculate the elapsed time elapsed since the last pose update, using the now and then times stored in the class.
Then, the code calculates the odometry by determining the distance traveled by each wheel based on the change in encoder ticks since the last update. If the encoder ticks have not changed since the last update (i.e., enc_left is None), then the distance traveled by both wheels is set to 0.
Finally, the code calculates the speed of each wheel based on the distance traveled and the elapsed time, and stores them in left_speed
and right_speed
variables respectively.
if (d != 0):
# calculate distance traveled in x and y
x = cos( th ) * d
y = -sin( th ) * d
# calculate the final position of the robot
self.x = self.x + ( cos( self.th ) * x - sin( self.th ) * y )
self.y = self.y + ( sin( self.th ) * x + cos( self.th ) * y )
if( th != 0):
self.th = self.th + th
# publish the odom information
quaternion = Quaternion()
quaternion.x = 0.0
quaternion.y = 0.0
quaternion.z = sin( self.th / 2 )
quaternion.w = cos( self.th / 2 )
self.odomBroadcaster.sendTransform(
(self.x, self.y, 0),
(quaternion.x, quaternion.y, quaternion.z, quaternion.w),
rospy.Time.now(),
self.base_frame_id,
self.odom_frame_id
)
odom = Odometry()
odom.header.stamp = now
odom.header.frame_id = self.odom_frame_id
odom.pose.pose.position.x = self.x
odom.pose.pose.position.y = self.y
odom.pose.pose.position.z = 0
odom.pose.pose.orientation = quaternion
odom.child_frame_id = self.base_frame_id
odom.twist.twist.linear.x = self.dx
odom.twist.twist.linear.y = 0
odom.twist.twist.angular.z = self.dr
self.odomPub.publish(odom)
This section of the code is responsible for calculating and publishing the odometry information of the robot.
If the robot has traveled a nonzero distance (d != 0)
, it calculates the distance traveled in the x and y directions using the current angle of the robot (th) and the distance traveled (d). It then updates the position of the robot by adding this distance to the current position.
If the robot has turned a nonzero angle (th != 0)
, it updates the current angle of the robot (self.th) by adding the angle turned (th).
Next, it creates a Quaternion object to represent the orientation of the robot, and sets its values based on the current angle of the robot.
Then, it publishes the odometry information as a transform from the base frame to the odometry frame, and as an Odometry message on the /odom
topic, with the position and orientation of the robot.
def lwheelCallback(self, msg):
enc = msg.data
if (enc < self.encoder_low_wrap and self.prev_lencoder > self.encoder_high_wrap):
self.lmult = self.lmult + 1
if (enc > self.encoder_high_wrap and self.prev_lencoder < self.encoder_low_wrap):
self.lmult = self.lmult - 1
self.left = 1.0 * (enc + self.lmult * (self.encoder_max - self.encoder_min))
self.prev_lencoder = enc
This is a callback function for the left wheel encoder subscriber. It receives a message from the topic to which it has subscribed, and extracts the data from it.
enc = msg.data
: extracts the data from the message and stores it in the enc variable.if (enc < self.encoder_low_wrap and self.prev_lencoder > self.encoder_high_wrap)
: checks if the encoder value has wrapped around the low end (underflow) and the previous value was above the high end (overflow). If true, increment the left encoder multiplier.if (enc > self.encoder_high_wrap and self.prev_lencoder < self.encoder_low_wrap)
: checks if the encoder value has wrapped around the high end (overflow) and the previous value was below the low end (underflow). If true, decrement the left encoder multiplier.self.left = 1.0 * (enc + self.lmult * (self.encoder_max - self.encoder_min))
:** calculates the left encoder count by adding the current encoder value to the left encoder multiplier times the range of the encoder values. The range is defined as the difference between the maximum and minimum encoder values.self.prev_lencoder = enc
: updates the previous encoder value with the current encoder value.