emulation_base.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import argparse
4 import copy
5 import math
6 
7 import rospy
8 import tf
9 import tf2_ros
10 from geometry_msgs.msg import Twist, TransformStamped, Pose
11 from nav_msgs.msg import Odometry
12 from std_srvs.srv import Trigger, TriggerRequest, TriggerResponse
13 
14 class EmulationBase(object):
15  def __init__(self, odom_frame):
16  # this node emulates the controllers of a base including twist_controller and odometry_controller
17  #
18  # interfaces
19  # - subscribers:
20  # - /base/twist_controller/command [geometry_msgs/Twist]
21  # - publishers:
22  # - /base/odometry_controller/odometry [nav_msgs/Odometry]
23  # - tf (odom_frame --> base_footprint)
24 
25  # TODO
26  # - speed factor
27 
28  self.odom_frame_ = odom_frame
29 
30  rospy.Subscriber("/base/twist_controller/command", Twist, self.twist_callback, queue_size=1)
31  self.pub_odom = rospy.Publisher("/base/odometry_controller/odometry", Odometry, queue_size=1)
32  rospy.Service("/base/odometry_controller/reset_odometry", Trigger, self.reset_odometry)
34 
35  self.timestamp_last_update = rospy.Time.now()
36 
37  self.twist = Twist()
38  self.timestamp_last_twist = rospy.Time(0)
39 
40  self.odom = Odometry()
41  self.odom.header.frame_id = self.odom_frame_
42  self.odom.child_frame_id = "base_footprint"
43  self.odom.pose.pose.orientation.w = 1 # initialize orientation with a valid quaternion
44 
45  rospy.Timer(rospy.Duration(0.1), self.timer_cb)
46 
47  rospy.loginfo("Emulation for base running")
48 
49  def reset_odometry(self, req):
50  self.odom.pose.pose = Pose()
51  self.odom.pose.pose.orientation.w = 1
52 
53  return TriggerResponse(True, "odometry resetted")
54 
55  def twist_callback(self, msg):
56  self.twist = msg
57  self.timestamp_last_twist = rospy.Time.now()
58 
59  def timer_cb(self, event):
60  # move robot (calculate new pose)
61  dt = rospy.Time.now() - self.timestamp_last_update
62  self.timestamp_last_update = rospy.Time.now()
63  time_since_last_twist = rospy.Time.now() - self.timestamp_last_twist
64 
65  #print "dt =", dt.to_sec(), ". duration since last twist =", time_since_last_twist.to_sec()
66  # we assume we're not moving any more if there is no new twist after 0.1 sec
67  if time_since_last_twist < rospy.Duration(0.1):
68  new_pose = copy.deepcopy(self.odom.pose.pose)
69  yaw = tf.transformations.euler_from_quaternion([self.odom.pose.pose.orientation.x, self.odom.pose.pose.orientation.y, self.odom.pose.pose.orientation.z, self.odom.pose.pose.orientation.w])[2] + self.twist.angular.z * dt.to_sec()
70  quat = tf.transformations.quaternion_from_euler(0, 0, yaw)
71  new_pose.orientation.x = quat[0]
72  new_pose.orientation.y = quat[1]
73  new_pose.orientation.z = quat[2]
74  new_pose.orientation.w = quat[3]
75  new_pose.position.x += self.twist.linear.x * dt.to_sec() * math.cos(yaw) - self.twist.linear.y * dt.to_sec() * math.sin(yaw)
76  new_pose.position.y += self.twist.linear.x * dt.to_sec() * math.sin(yaw) + self.twist.linear.y * dt.to_sec() * math.cos(yaw)
77  self.odom.pose.pose = new_pose
78 
79  # we're moving, so we set a non-zero twist
80  self.odom.twist.twist.linear.x = self.twist.linear.x
81  self.odom.twist.twist.linear.y = self.twist.linear.y
82  self.odom.twist.twist.angular.z = self.twist.angular.z
83  else:
84  # reset twist as we're not moving anymore
85  self.odom.twist.twist = Twist()
86 
87  # publish odometry
88  odom = copy.deepcopy(self.odom)
89  odom.header.stamp = rospy.Time.now() # update to current time stamp
90  self.pub_odom.publish(odom)
91 
92  # publish tf
93  # pub base_footprint --> odom_frame
94  t_odom = TransformStamped()
95  t_odom.header.stamp = rospy.Time.now()
96  t_odom.header.frame_id = self.odom_frame_
97  t_odom.child_frame_id = "base_footprint"
98  t_odom.transform.translation = self.odom.pose.pose.position
99  t_odom.transform.rotation = self.odom.pose.pose.orientation
100 
101  transforms = [t_odom]
102 
103  self.br.sendTransform(transforms)
104 
105 if __name__ == '__main__':
106  rospy.init_node('emulation_base')
107  parser = argparse.ArgumentParser(conflict_handler='resolve',
108  description="Tool for emulating base by publishing odometry and propagating base_footprint.")
109  parser.add_argument('-o', '--odom_frame', help='odom frame name (default: \'odom_combined\')', default='odom_combined')
110  args, unknown = parser.parse_known_args()
111  EmulationBase(args.odom_frame)
112  rospy.spin()
emulation_base.EmulationBase.pub_odom
pub_odom
Definition: emulation_base.py:31
emulation_base.EmulationBase.reset_odometry
def reset_odometry(self, req)
Definition: emulation_base.py:49
emulation_base.EmulationBase.timestamp_last_twist
timestamp_last_twist
Definition: emulation_base.py:38
emulation_base.EmulationBase.odom
odom
Definition: emulation_base.py:40
emulation_base.EmulationBase.__init__
def __init__(self, odom_frame)
Definition: emulation_base.py:15
emulation_base.EmulationBase.timestamp_last_update
timestamp_last_update
Definition: emulation_base.py:35
emulation_base.EmulationBase.twist
twist
Definition: emulation_base.py:37
emulation_base.EmulationBase.twist_callback
def twist_callback(self, msg)
Definition: emulation_base.py:55
tf2_ros::TransformBroadcaster
emulation_base.EmulationBase.odom_frame_
odom_frame_
Definition: emulation_base.py:28
emulation_base.EmulationBase.timer_cb
def timer_cb(self, event)
Definition: emulation_base.py:59
emulation_base.EmulationBase
Definition: emulation_base.py:14
emulation_base.EmulationBase.br
br
Definition: emulation_base.py:33


cob_hardware_emulation
Author(s): Florian Weisshardt
autogenerated on Mon May 1 2023 02:44:27