sim_adapter.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # Software License Agreement (BSD License)
00004 #
00005 #  Copyright (c) 2011, UC Regents
00006 #  All rights reserved.
00007 #
00008 #  Redistribution and use in source and binary forms, with or without
00009 #  modification, are permitted provided that the following conditions
00010 #  are met:
00011 #
00012 #   * Redistributions of source code must retain the above copyright
00013 #     notice, this list of conditions and the following disclaimer.
00014 #   * Redistributions in binary form must reproduce the above
00015 #     copyright notice, this list of conditions and the following
00016 #     disclaimer in the documentation and/or other materials provided
00017 #     with the distribution.
00018 #   * Neither the name of the University of California nor the names of its
00019 #     contributors may be used to endorse or promote products derived
00020 #     from this software without specific prior written permission.
00021 #
00022 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 #  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 #  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 #  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 #  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 #  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 #  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 #  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 #  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 #  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 #  POSSIBILITY OF SUCH DAMAGE.
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         # Publishers
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         # Subscribers
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     # Subscriber callbacks:
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             #rospy.loginfo('Motor enable: ' + str(msg.data))
00116             self.motor_enable = msg.data
00117     
00118     # Timer callbacks:
00119     def simulation_timer_callback(self, event):
00120         if False:
00121             print event.__dict__
00122 #            print 'last_expected:        ', event.last_expected
00123 #            print 'last_real:            ', event.last_real
00124 #            print 'current_expected:     ', event.current_expected
00125 #            print 'current_real:         ', event.current_real
00126 #            print 'current_error:        ', (event.current_real - event.current_expected).to_sec()
00127 #            print 'profile.last_duration:', event.last_duration.to_sec()
00128 #            if event.last_real:
00129 #                print 'last_error:           ', (event.last_real - event.last_expected).to_sec(), 'secs'
00130 #            print
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         #rospy.loginfo("position: " + str(self.position) + " velocity: " + str(self.velocity) + " thrust_cmd: " + str(self.thrust_cmd))
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         # The following model is completely arbitrary and should not be taken to be representative of
00145         # real vehicle performance!
00146         # But, it should be good enough to test out control modes etc.
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         #rospy.loginfo("thrust_cmd = %f, dt = %f" % (self.thrust_cmd, dt))
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         #rospy.loginfo("T_ned_imu = \n" + str(T_ned_imu))
00176         if self.T_imu_vicon is None:
00177             # grab the static transform from imu to vicon frame from param server:
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') # xyzw
00181             R_vicon_imu = tft.quaternion_matrix(q_vicon_imu)
00182             t_vicon_imu = tft.translation_matrix(frames['vicon_to_imu']['translation'])
00183 #            rospy.loginfo(str(R_vicon_imu))
00184 #            rospy.loginfo(str(t_vicon_imu))
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             #rospy.loginfo(str(T_vicon_imu))
00191         # we have /ned -> /imu, need to output /enu -> /vicon:
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()


simulated_quadrotor
Author(s): bouffard
autogenerated on Sun Jan 5 2014 11:38:50