asctec_control_plots.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Software License Agreement (BSD License)
00003 #
00004 #  Copyright (c) 2011, UC Regents
00005 #  All rights reserved.
00006 #
00007 #  Redistribution and use in source and binary forms, with or without
00008 #  modification, are permitted provided that the following conditions
00009 #  are met:
00010 #
00011 #   * Redistributions of source code must retain the above copyright
00012 #     notice, this list of conditions and the following disclaimer.
00013 #   * Redistributions in binary form must reproduce the above
00014 #     copyright notice, this list of conditions and the following
00015 #     disclaimer in the documentation and/or other materials provided
00016 #     with the distribution.
00017 #   * Neither the name of the University of California nor the names of its
00018 #     contributors may be used to endorse or promote products derived
00019 #     from this software without specific prior written permission.
00020 #
00021 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 #  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 #  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 #  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 #  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 #  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 #  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 #  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 #  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 #  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 #  POSSIBILITY OF SUCH DAMAGE.
00033 
00034 import roslib
00035 roslib.load_manifest('starmac_tools')
00036 
00037 import sys
00038 from optparse import OptionParser
00039 import scipy.io as sio
00040 import numpy as np
00041 from pylab import plt
00042 
00043 from starmac_tools.load_bag import BagLoader
00044 from starmac_tools.postprocessing.generic import Dummy
00045 from starmac_tools.postprocessing.state import process_state
00046 from starmac_tools.postprocessing.control import process_control_mode_output
00047 from starmac_tools.postprocessing.imu import process_imu
00048 from starmac_tools.postprocessing.asctec import process_asctec_ctrl_input
00049 from starmac_tools.plotting.util import newfig, show, plot_traj, save_all_figs
00050 from starmac_tools.plotting.timeseries_cursor import TimeSeriesCursorSynchronizer
00051 from starmac_tools.timeseries import uniform_resample
00052 
00053 TRAJ_PLOT_OPTIONS = dict(label='trajectory', ls='-', marker='.', color='blue', lw=1, ms=1.0, mec='blue', 
00054                          mew=0.5,  mfc='None', alpha=0.5)
00055 
00056 class AscTecControlPlots(object):
00057     processing_key_cb = False
00058     def __init__(self, fname, t_start=None, t_end=None, do_plots=True):
00059         self.input_bag = BagLoader(fname)
00060         self.t_start = t_start
00061         self.t_end = t_end
00062         self.process_data()
00063         if do_plots:
00064             self.do_plots()
00065         
00066     def show(self):
00067         plt.show()
00068         
00069     def process_data(self):
00070         v = Dummy()
00071         v.state = Dummy()
00072         v.control = Dummy()
00073         v.imu = Dummy()
00074         v.asctec_ctrl_input = Dummy()
00075         
00076         # kludge:
00077         if 'odom' in self.input_bag.get_topics():
00078             odom_topic = 'odom'
00079         elif 'estimator/output' in self.input_bag.get_topics():
00080             odom_topic = 'estimator/output'
00081         # following should not be here!!!
00082         elif 'dual_ekf_node/output' in self.input_bag.get_topics():
00083             odom_topic = 'dual_ekf_node/output'
00084         else:
00085             raise "Can't find an odometry topic"
00086         process_state(self.input_bag, odom_topic, v.state)
00087         process_control_mode_output(self.input_bag, 'controller_mux/output', v.control)
00088         process_imu(self.input_bag, 'asctec/imu', v.imu)
00089         process_asctec_ctrl_input(self.input_bag, 'asctec/CTRL_INPUT', v.asctec_ctrl_input)
00090         tout, data_out = uniform_resample((('linear', v.imu.t[1:], v.imu.ori_ypr[1:,0]), 
00091                                            ('linear', v.state.t[1:], v.state.ori_ypr[1:,0])), 0.02)
00092         self.v = v
00093 
00094         # setup istart and iend for each group of data:
00095         vars = (self.v.control, self.v.imu, self.v.asctec_ctrl_input, self.v.state)
00096         if self.t_start is not None:
00097             for w in vars:
00098                 w.istart = np.where(w.t >= self.t_start)[0][0]
00099         else:
00100             for w in vars:
00101                 w.istart = 1
00102                 
00103         if self.t_end is not None:
00104             for w in vars:
00105                 w.iend = np.where(w.t >= self.t_end)[0][0]
00106         else:
00107             for w in vars:
00108                 w.iend = None
00109                 
00110         
00111     def do_plots(self):
00112         self.cur_sync = TimeSeriesCursorSynchronizer()
00113         self.plot_axis_control('roll')
00114         self.plot_axis_control('pitch')
00115         self.plot_axis_control('yaw')
00116         self.plot_alt_control()
00117         self.plot_top_view()
00118         self.plot_side_view_east()
00119         self.plot_side_view_north()
00120         self.plot_east_vs_time()
00121         self.plot_north_vs_time()
00122         self.plot_r_vs_time()
00123         self.plot_r_histogram()
00124         
00125     def _timeseries_click_cb(self, event):
00126         if event.button == 2:
00127             #print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' \
00128             #    % (event.button, event.x, event.y, event.xdata, event.ydata)
00129             if event.key is None:
00130                 self.cur_sync.set_cursors(event.xdata)
00131             elif event.key == 'shift':
00132                 self.cur_sync.add_axvline(event.xdata)
00133             elif event.key == 'control':
00134                 self.cur_sync.sync_xrange(event.inaxes)
00135             else:
00136                 pass
00137             
00138     def _timeseries_key_release_cb(self, event):
00139         print "Handling key callback, event.key = ", event.key, " event.inaxes = ", id(event.inaxes)
00140         if not self.processing_key_cb:
00141             self.processing_key_cb = True
00142             if event.key == 'x': # Clear all axvlines
00143                 self.cur_sync.clear_axvlines()
00144             elif event.key == 'L': # Toggle legend
00145                 if event.inaxes.get_legend() == None:
00146                     event.inaxes.legend()
00147                 else:
00148                     event.inaxes.legend_ = None
00149                 event.inaxes.get_figure().canvas.draw()
00150             self.processing_key_cb = False
00151             
00152 
00153     def _timeseries_postplot(self):
00154         ax = plt.gca()
00155         fig = ax.get_figure()
00156         self.cur_sync.add_axis(ax)
00157         cid_btn = fig.canvas.mpl_connect('button_press_event', self._timeseries_click_cb)
00158         cid_key = fig.canvas.mpl_connect('key_release_event', self._timeseries_key_release_cb)
00159         
00160     def plot_axis_control(self, axis, 
00161                           show_mux_cmd=True, show_asctec_cmd=True, show_vicon_meas=True, show_imu_meas=True):
00162         axismap = {'roll': (self.v.control.roll_cmd, 2, +1, self.v.asctec_ctrl_input.roll_cmd, -1),
00163                    'pitch': (self.v.control.pitch_cmd, 1, -1, self.v.asctec_ctrl_input.pitch_cmd, -1),
00164                    'yaw': (self.v.control.yaw_cmd, 0, -1, self.v.asctec_ctrl_input.yaw_rate_cmd, +1) # not sure about the multiplier here
00165                    }
00166         control_mode_cmd, state_axis, imu_mult, asctec_cmd, asctec_cmd_mult = axismap[axis]
00167         newfig("%s Axis Control" % axis.capitalize(), "time [s]", "%s [deg]" % axis.capitalize())
00168         # np.clip() and the [1:] stuff in the following to attempt deal with bogus initial data points in IMU data:
00169         if show_mux_cmd:
00170             plt.plot(self.v.control.t[self.v.control.istart:self.v.control.iend],
00171                      control_mode_cmd[self.v.control.istart:self.v.control.iend], label='cmd (from mux)')
00172         if show_vicon_meas:
00173             plt.plot(self.v.state.t[self.v.state.istart:self.v.state.iend], 
00174                      self.v.state.ori_ypr[self.v.state.istart:self.v.state.iend, state_axis], label='meas (Vicon)')
00175         if show_imu_meas:
00176             plt.plot(np.clip(self.v.imu.t[self.v.imu.istart:self.v.imu.iend], 0, np.inf), 
00177                      imu_mult*self.v.imu.ori_ypr[self.v.imu.istart:self.v.imu.iend, state_axis], label='meas (IMU)')
00178         if show_asctec_cmd and axis is not 'yaw':
00179             plt.plot(self.v.asctec_ctrl_input.t[self.v.asctec_ctrl_input.istart:self.v.asctec_ctrl_input.iend], 
00180                      asctec_cmd_mult*asctec_cmd[self.v.asctec_ctrl_input.istart:self.v.asctec_ctrl_input.iend],
00181                     label='cmd (AscTec)')
00182         # Plot difference between vicon and imu: (broken, comment it out for now..)
00183 #        tout, data_out = uniform_resample((('linear', self.v.imu.t[self.v.asctec_ctrl_input.istart:self.v.asctec_ctrl_input.iend], 
00184 #                                                      self.v.imu.ori_ypr[self.v.asctec_ctrl_input.istart:self.v.asctec_ctrl_input.iend,state_axis]), 
00185 #                                           ('linear', self.v.state.t[self.v.state.istart:self.v.state.iend], 
00186 #                                                      self.v.state.ori_ypr[self.v.state.istart:self.v.state.iend, state_axis])), 
00187 #                                           0.02)
00188 #        plt.plot(tout, imu_mult*data_out[0][0] - data_out[1][0], label='IMU - Vicon')
00189         plt.legend()
00190         self._timeseries_postplot()
00191         
00192     def plot_alt_control(self):
00193         newfig("Altitude Control", "time [s]", "Alt [m]")
00194         plt.plot(self.v.control.t[self.v.control.istart:self.v.control.iend], self.v.control.alt_cmd[self.v.control.istart:self.v.control.iend], label="cmd")
00195         plt.plot(self.v.state.t[self.v.state.istart:self.v.state.iend], self.v.state.up[self.v.state.istart:self.v.state.iend], label="meas (Vicon)")
00196         plt.legend()
00197         self._timeseries_postplot()
00198         
00199     def plot_top_view(self):
00200         newfig('Top View', 'East [m]', 'North [m]', equal_axes=True)
00201         plot_traj(self.v.state.east[self.v.state.istart:self.v.state.iend], 
00202                   self.v.state.north[self.v.state.istart:self.v.state.iend], **TRAJ_PLOT_OPTIONS)
00203         
00204     def plot_side_view_east(self):
00205         newfig('Side View (East)', 'East [m]', 'Up [m]', equal_axes=True)
00206         plot_traj(self.v.state.east[self.v.state.istart:self.v.state.iend], 
00207                   self.v.state.up[self.v.state.istart:self.v.state.iend], **TRAJ_PLOT_OPTIONS)
00208         
00209     def plot_side_view_north(self):
00210         newfig('Side View (North)', 'North [m]', 'Up [m]', equal_axes=True)
00211         plot_traj(self.v.state.north[self.v.state.istart:self.v.state.iend], 
00212                   self.v.state.up[self.v.state.istart:self.v.state.iend], **TRAJ_PLOT_OPTIONS)
00213         
00214     def plot_east_vs_time(self):
00215         newfig('East vs. Time', 'time [s]', 'East [m]')
00216         plot_traj(self.v.state.t[self.v.state.istart:self.v.state.iend], 
00217                   self.v.state.east[self.v.state.istart:self.v.state.iend], label="meas (Vicon)")
00218         self._timeseries_postplot()
00219         
00220     def plot_north_vs_time(self):
00221         newfig('North vs. Time', 'time [s]', 'North [m]')
00222         plot_traj(self.v.state.t[self.v.state.istart:self.v.state.iend], 
00223                   self.v.state.north[self.v.state.istart:self.v.state.iend], label="meas (Vicon)")
00224         self._timeseries_postplot()
00225         
00226     def plot_r_vs_time(self):
00227         newfig('sqrt(x^2 + y^2) vs. Time', 'time [s]', 'North [m]')
00228         t = self.v.state.t[self.v.state.istart:self.v.state.iend]
00229         r = np.sqrt(self.v.state.north[self.v.state.istart:self.v.state.iend]**2 + \
00230                     self.v.state.east[self.v.state.istart:self.v.state.iend]**2 )
00231         plot_traj(t, r, label="meas (Vicon)")
00232         self._timeseries_postplot()
00233         
00234     def plot_r_histogram(self):
00235         newfig('Histogram of sqrt(x^2 + y^2)', 'distance from origin [m]', 'Count')
00236         t = self.v.state.t[self.v.state.istart:self.v.state.iend]
00237         r = np.sqrt(self.v.state.north[self.v.state.istart:self.v.state.iend]**2 + \
00238                     self.v.state.east[self.v.state.istart:self.v.state.iend]**2 )
00239         plt.hist(r,50)
00240         
00241     def save_all_figs(self, output_dir, prefix="", ext=".eps", **savefig_kwargs):
00242         save_all_figs(output_dir, prefix=prefix, ext=ext, **savefig_kwargs)
00243         
00244         
00245 if __name__ == "__main__":
00246     parser = OptionParser(usage="usage: %prog [options] bagfile")
00247     parser.add_option('-s', '--start', dest='t_start')
00248     parser.add_option('-e', '--end', dest='t_end')
00249     (options, args) = parser.parse_args()
00250     t_start = None
00251     t_end = None
00252     if options.t_start is not None:
00253         t_start = float(options.t_start)
00254     if options.t_end is not None:
00255         t_end = float(options.t_end)
00256     self = AscTecControlPlots(args[0], t_start, t_end)
00257     self.show()
00258     


starmac_tools
Author(s): bouffard
autogenerated on Sun Jan 5 2014 11:38:35