00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 import roslib
00036 roslib.load_manifest('simulated_quadrotor')
00037 import rospy
00038 import numpy as np
00039 from math import sin, cos, radians, degrees
00040 import tf.transformations as tft
00041 from tf import TransformListener
00042 
00043 from nav_msgs.msg import Odometry
00044 from geometry_msgs.msg import TransformStamped
00045 from std_msgs.msg import Bool
00046 from flyer_controller.msg import control_mode_output
00047 
00048 from starmac_roslib.pid import PidController
00049 
00050 from simulated_quadrotor.models.simple_nonlinear import SimpleNonlinearModel
00051 from simulated_quadrotor.models.linear import LinearModel
00052 
00053 class SimAdapter(object):
00054 
00055     def __init__(self):
00056         pass
00057         
00058     def start(self):
00059         rospy.init_node('sim_adapter')
00060         self.init_params()
00061         self.init_state()
00062         self.init_vars()
00063         self.init_publishers()
00064         self.init_subscribers()
00065         self.init_timers()
00066         rospy.spin()
00067         
00068     def init_params(self):
00069         self.param_model = rospy.get_param("~model", "simple_nonlinear")
00070         self.rate = rospy.get_param("~rate", 40)
00071         self.publish_state_estimate = rospy.get_param("~publish_state_estimate", True)
00072         self.publish_odometry_messages = rospy.get_param("~publish_odometry", True)
00073         self.sim_odom_topic = rospy.get_param("~sim_odom_topic", "odom")
00074         
00075     def init_state(self):
00076         if self.param_model == "simple_nonlinear":
00077             self.model = SimpleNonlinearModel()
00078         elif self.param_model == "linear":
00079             self.model = LinearModel()
00080         else:
00081             rospy.logerror("Model type '%s' unknown" % self.model)
00082             raise Exception("Model type '%s' unknown" % self.model)
00083 
00084     def init_vars(self):
00085         self.latest_cmd_msg = control_mode_output()
00086         self.motor_enable = False
00087         self.thrust_cmd = 0.0
00088         self.tfl = TransformListener()
00089         self.T_vicon_imu = None
00090         self.T_imu_vicon = None
00091         self.T_enu_ned = None
00092         
00093     def init_publishers(self):
00094         
00095         if self.publish_odometry_messages: 
00096             self.pub_odom = rospy.Publisher(self.sim_odom_topic, Odometry)
00097         if self.publish_state_estimate:
00098             self.pub_state = rospy.Publisher('state', TransformStamped)
00099         
00100     def init_subscribers(self):
00101         
00102         self.control_input_sub = rospy.Subscriber('controller_mux/output', control_mode_output, self.control_input_callback)
00103         self.motor_enable_sub = rospy.Subscriber('teleop_flyer/motor_enable', Bool, self.motor_enable_callback)
00104       
00105     def init_timers(self):
00106         self.simulation_timer = rospy.Timer(rospy.Duration(1.0/self.rate), self.simulation_timer_callback)
00107                                       
00108     
00109     def control_input_callback(self, msg):
00110         rospy.logdebug('Current command is: ' + str(msg))
00111         self.latest_cmd_msg = msg
00112     
00113     def motor_enable_callback(self, msg):
00114         if msg.data != self.motor_enable:
00115             
00116             self.motor_enable = msg.data
00117     
00118     
00119     def simulation_timer_callback(self, event):
00120         if False:
00121             print event.__dict__
00122 
00123 
00124 
00125 
00126 
00127 
00128 
00129 
00130 
00131         if event.last_real is None:
00132             dt = 0.0
00133         else:
00134             dt = (event.current_real - event.last_real).to_sec()
00135             self.update_controller(dt)
00136             self.update_state(dt)
00137         
00138         if self.publish_odometry_messages:
00139             self.publish_odometry()
00140         if self.publish_state_estimate:
00141             self.publish_state(event.current_real)
00142         
00143     def update_state(self, dt):
00144         
00145         
00146         
00147         self.model.update(dt)
00148             
00149     def update_controller(self, dt):
00150         lcm = self.latest_cmd_msg
00151         self.model.set_input(lcm.motors_on, lcm.roll_cmd, lcm.pitch_cmd, lcm.direct_yaw_rate_commands, 
00152                   lcm.yaw_cmd, lcm.yaw_rate_cmd, lcm.direct_thrust_commands, lcm.alt_cmd, lcm.thrust_cmd)
00153         
00154                     
00155     def publish_odometry(self):
00156         odom_msg = Odometry()
00157         odom_msg.header.stamp = rospy.Time.now()
00158         odom_msg.header.frame_id = "/ned"
00159         odom_msg.child_frame_id = "/simflyer1/flyer_imu"
00160         oppp = odom_msg.pose.pose.position
00161         oppp.x, oppp.y, oppp.z  = self.model.get_position()
00162         ottl = odom_msg.twist.twist.linear
00163         ottl.x, ottl.y, ottl.z = self.model.get_velocity()
00164         oppo = odom_msg.pose.pose.orientation
00165         oppo.x, oppo.y, oppo.z, oppo.w = self.model.get_orientation()
00166         otta = odom_msg.twist.twist.angular
00167         otta.x, otta.y, otta.z = self.model.get_angular_velocity()
00168         self.pub_odom.publish(odom_msg)
00169         
00170     def publish_state(self, t):
00171         state_msg = TransformStamped()
00172         t_ned_imu = tft.translation_matrix(self.model.get_position())
00173         R_ned_imu = tft.quaternion_matrix(self.model.get_orientation())
00174         T_ned_imu = tft.concatenate_matrices(t_ned_imu, R_ned_imu)
00175         
00176         if self.T_imu_vicon is None:
00177             
00178             frames = rospy.get_param("frames", None)
00179             ypr = frames['vicon_to_imu']['rotation']
00180             q_vicon_imu = tft.quaternion_from_euler(*[radians(x) for x in ypr], axes='rzyx') 
00181             R_vicon_imu = tft.quaternion_matrix(q_vicon_imu)
00182             t_vicon_imu = tft.translation_matrix(frames['vicon_to_imu']['translation'])
00183 
00184 
00185             self.T_vicon_imu = tft.concatenate_matrices(t_vicon_imu, R_vicon_imu)
00186             self.T_imu_vicon = tft.inverse_matrix(self.T_vicon_imu)
00187             self.T_enu_ned = tft.euler_matrix(radians(90), 0, radians(180), 'rzyx')
00188             rospy.loginfo(str(self.T_enu_ned))
00189             rospy.loginfo(str(self.T_imu_vicon))
00190             
00191         
00192         T_enu_vicon = tft.concatenate_matrices(self.T_enu_ned, T_ned_imu, self.T_imu_vicon )
00193         state_msg.header.stamp  = t
00194         state_msg.header.frame_id = "/enu"
00195         state_msg.child_frame_id = "/simflyer1/flyer_vicon"
00196         stt = state_msg.transform.translation
00197         stt.x, stt.y, stt.z = T_enu_vicon[:3,3]
00198         stro = state_msg.transform.rotation
00199         stro.x, stro.y, stro.z, stro.w = tft.quaternion_from_matrix(T_enu_vicon)
00200         
00201         self.pub_state.publish(state_msg)
00202         
00203     def get_transform(self, tgt, src):
00204         t = self.tfl.getLatestCommonTime(tgt, src)
00205         t_src_tgt, q_src_tgt = self.tfl.lookupTransform(tgt, src, t)
00206         T_src_tgt =self.tfl.fromTranslationRotation(t_src_tgt, q_src_tgt)
00207         return T_src_tgt 
00208         
00209 if __name__ == "__main__":
00210   self = SimAdapter()
00211   self.start()