00001
00002
00003 import matplotlib
00004 matplotlib.interactive( True )
00005 matplotlib.use( 'WXAgg' )
00006
00007 import numpy as np
00008 import wx
00009 import sys
00010
00011 import roslib
00012 roslib.load_manifest( 'rospy' )
00013 roslib.load_manifest( 'sensor_msgs' )
00014 roslib.load_manifest( 'std_msgs' )
00015
00016
00017 import rospy
00018 from sensor_msgs.msg import JointState
00019 from std_msgs.msg import Int8
00020
00021 from threading import Thread, Lock
00022
00023 class WXMatPlotLibPanel( wx.Panel ):
00024 def __init__( self, parent, color=None, dpi=None, **kwargs ):
00025 from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
00026 from matplotlib.figure import Figure
00027
00028
00029 if 'id' not in kwargs.keys():
00030 kwargs['id'] = wx.ID_ANY
00031 if 'style' not in kwargs.keys():
00032 kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE
00033 wx.Panel.__init__( self, parent, **kwargs )
00034
00035
00036 self.figure = Figure( None, dpi )
00037 self.canvas = FigureCanvasWxAgg( self, -1, self.figure )
00038
00039
00040 self._SetSize()
00041 self.draw()
00042
00043 self._resizeflag = False
00044
00045 self.Bind(wx.EVT_IDLE, self._onIdle)
00046 self.Bind(wx.EVT_SIZE, self._onSize)
00047
00048 def SetColor( self, rgbtuple=None ):
00049 """Set figure and canvas colours to be the same."""
00050 if rgbtuple is None:
00051 rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get()
00052 clr = [c/255. for c in rgbtuple]
00053 self.figure.set_facecolor( clr )
00054 self.figure.set_edgecolor( clr )
00055 self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) )
00056
00057 def _onSize( self, event ):
00058 self._resizeflag = True
00059
00060 def _onIdle( self, evt ):
00061 if self._resizeflag:
00062 self._resizeflag = False
00063 self._SetSize()
00064
00065 def _SetSize( self ):
00066 pixels = tuple( self.parent.GetClientSize() )
00067 self.SetSize( pixels )
00068 self.canvas.SetSize( pixels )
00069 self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(),
00070 float( pixels[1] )/self.figure.get_dpi() )
00071
00072 def draw(self): pass
00073
00074 class JointStateVizPanel( WXMatPlotLibPanel ):
00075 def __init__( self, parent, **kwargs ):
00076 self.lock = Lock()
00077 self.parent = parent
00078 self.positions = None
00079 self.velocities = None
00080 self.efforts = None
00081 WXMatPlotLibPanel.__init__( self, parent, **kwargs )
00082
00083
00084 def init_plots( self, names, limits ):
00085 N = len( names )
00086 self.axes = []
00087 self.lines = []
00088 self.scatter = []
00089 for j in range( N ):
00090 axes = self.figure.add_subplot( 3, N, j + 1 )
00091 axes.set_title( names[j] )
00092 upper = -np.pi
00093 lower = np.pi
00094
00095 if limits[j].has_key( 'upper' ):
00096 upper = limits[j].get( 'upper' )
00097
00098 if limits[j].has_key( 'lower' ):
00099 lower = limits[j].get( 'lower' )
00100
00101 axes.set_ylim( lower, upper )
00102 axes.set_xticks( [] )
00103
00104 self.lines.append( axes.plot( [0], 'r' )[0] )
00105
00106 axes.autoscale_view( scalex = False, scaley = False )
00107
00108 self.axes.append( axes )
00109
00110 for j in range( N ):
00111 axes = self.figure.add_subplot( 3, N, j + N + 1 )
00112 effort = 30
00113
00114 if limits[j].has_key( 'effort' ):
00115 effort = limits[j].get( 'effort' )
00116
00117 axes.set_ylim( -effort, effort )
00118 axes.set_xticks( [] )
00119
00120 self.lines.append( axes.plot( [0], 'b' )[0] )
00121
00122 self.axes.append( axes )
00123
00124 for j in range( N ):
00125 axes = self.figure.add_subplot( 3, N, j + 2 * N + 1 )
00126 upper = -2 * np.pi
00127 lower = 2 * np.pi
00128 velocity = 2 * np.pi
00129
00130 if limits[j].has_key( 'upper' ):
00131 upper = limits[j].get( 'upper' )
00132
00133 if limits[j].has_key( 'lower' ):
00134 lower = limits[j].get( 'lower' )
00135
00136 if limits[j].has_key( 'velocity' ):
00137 velocity = limits[j].get( 'velocity' )
00138
00139 axes.set_ylim( -velocity, velocity )
00140 axes.set_xlim( lower, upper )
00141
00142 self.scatter.append( axes.scatter( [0], [0],
00143 c = 'b',
00144 s = 2.0,
00145 marker = 'x') )
00146
00147 self.axes.append( axes )
00148
00149
00150
00151
00152 def update_plots( self ):
00153
00154
00155 if not hasattr( self, 'axes' ):
00156 return
00157
00158 if self.positions is None or len( self.positions ) == 0:
00159 return
00160
00161 ( l, N ) = self.positions.shape
00162
00163 for j in range( N ):
00164 axes = self.axes[j]
00165 axes.lines[0].remove()
00166 axes.plot( self.positions[:,j], 'r')
00167
00168 for j in range( N ):
00169 axes = self.axes[j + N]
00170 axes.lines[0].remove()
00171 axes.plot( self.efforts[:,j], 'b')
00172
00173
00174 for j in range( N ):
00175 axes = self.axes[j + N * 2]
00176 self.scatter[j].remove()
00177 self.scatter[j] = axes.scatter( self.positions[:,j],
00178 self.velocities[:,j],
00179 c = 'b',
00180 s = 2.0,
00181 marker = 'x')
00182
00183
00184
00185 def draw( self ):
00186 self.update_plots()
00187
00188 def setData( self, pos, vel, eff, duration ):
00189 self.positions = pos
00190 self.velocities = vel
00191 self.efforts = eff
00192 self.duration = duration
00193 self.draw()
00194
00195 class JointStateVizFrame( wx.Frame ) :
00196 def __init__( self, title, ns, js_topic ):
00197 wx.Frame.__init__( self, None, wx.ID_ANY, title, size = (2000, 800) )
00198 self.CreateStatusBar()
00199 self.SetStatusText( 'waiting for data on %s ...'%js_topic )
00200 self.panel = JointStateVizPanel( self )
00201 self.Show()
00202
00203 self.js_sub = rospy.Subscriber( js_topic, JointState, self.js_callback )
00204 self.update_sub = rospy.Subscriber( ''.join( (ns, '/update' ) ), Int8, self.update_callback )
00205
00206
00207 (self.joint_names, self.limits) = self.load_params( ns )
00208
00209 self.idx = None
00210 self.joint_states = []
00211
00212 self.panel.init_plots( self.joint_names, self.limits )
00213
00214 def load_params( self, ns ):
00215 params = rospy.get_param( ns )
00216 joint_names = params.keys()
00217
00218 limits = []
00219 for joint_name in joint_names:
00220 limits.append( params.get( joint_name ) )
00221
00222 return ( joint_names, limits )
00223
00224 def js_callback( self, msg ):
00225 self.joint_states.append( msg )
00226
00227 def update_callback( self, msg ):
00228 if msg.data == 0:
00229 self.joint_states = []
00230 elif msg.data == 1:
00231 self.update()
00232
00233 def update( self ):
00234 if self.joint_names is None or len( self.joint_names ) == 0 or len( self.joint_states ) == 0:
00235 return
00236 if self.idx is None:
00237 self.idx = map( self.joint_states[0].name.index, self.joint_names )
00238
00239 positions = []
00240 velocities = []
00241 efforts = []
00242
00243 for js in self.joint_states:
00244 pos = map( lambda x: js.position[x], self.idx )
00245 vel = map( lambda x: js.velocity[x], self.idx )
00246 effort = map( lambda x: js.effort[x], self.idx )
00247 positions.append( pos )
00248 velocities.append( vel )
00249 efforts.append( effort )
00250
00251 start_time = self.joint_states[0].header.stamp
00252 stop_time = self.joint_states[-1].header.stamp
00253 duration = stop_time - start_time
00254
00255 self.panel.setData( np.asarray( positions ),
00256 np.asarray( velocities ),
00257 np.asarray( efforts ),
00258 duration.to_sec())
00259
00260
00261
00262
00263
00264 status = 'ros-time: %s, duration: %5.3f sec'%( self.joint_states[0].header.stamp, duration.to_sec() )
00265
00266 self.SetStatusText( status )
00267
00268 self.joint_states = []
00269
00270
00271
00272
00273 arm_joint_names = ['lr_shoulder_pan_joint',
00274 'lr_shoulder_lift_joint',
00275 'lr_upper_arm_roll_joint',
00276 'lr_elbow_flex_joint',
00277 'lr_forearm_roll_joint',
00278 'lr_wrist_flex_joint',
00279 'lr_wrist_roll_joint']
00280
00281 l_arm_joint_names = map(lambda s: s.replace('lr_', 'l_'), arm_joint_names )
00282 r_arm_joint_names = map(lambda s: s.replace('lr_', 'r_'), arm_joint_names )
00283
00284
00285 if __name__ == '__main__':
00286 rospy.init_node( 'joint_state_viz_node' )
00287 app = wx.PySimpleApp( 0 )
00288 frame = JointStateVizFrame( 'Joint-State-Viz', '/joint_state_viz', '/joint_states' )
00289 app.MainLoop()
00290
00291
00292
00293