Difference between revisions of "UWAR:Tutorial:Matlab"

From PublicWiki
Jump to: navigation, search
Line 15: Line 15:
 
http://www.cs.washington.edu/lab/remote-access/xclients.html
 
http://www.cs.washington.edu/lab/remote-access/xclients.html
  
== Tutorial Data ==
+
== Adding UWAR Support ==
  
Looking for UWAR Trace files to use in this tutorial?  Check out files in:
+
To add UWAR functions to Matlab, add the following path in Matlab:
  
     /projects/ubicomp/uwar/data/Tutorials
+
     >> addpath('/projects/ubicomp/uwar/bin/matlab2');
  
 
== Summary of UWAR Matlab Functions ==
 
== Summary of UWAR Matlab Functions ==
Line 27: Line 27:
 
* IO Functions:
 
* IO Functions:
 
** <tt>uwar_io_init</tt> - Loads the UWAR Java IO library.  Only call this directly if you are using a custom jar (that should be very few of you)
 
** <tt>uwar_io_init</tt> - Loads the UWAR Java IO library.  Only call this directly if you are using a custom jar (that should be very few of you)
** <tt>uwar_io_loadFile</tt> - Load a UWAR trace file into memory for processing
+
** <tt>uwar_io_getSensorIds</tt> - List the available sensor data in a UWAR trace
** <tt>uwar_io_getData</tt> - Extract sensor data from a loaded UWAR trace
+
** <tt>uwar_io_getSensorData</tt> - Extract sensor data from a UWAR trace
 +
 
 +
== Tutorial Data ==
  
* Activity Recognition Functions:
+
Looking for UWAR Trace files to use in this tutorial? Check out files in:
** <tt>uwar_core_getCompleteData</tt> - generates a <tt>completeData</tt> struct (see [http://embedded.seattle.intel-research.net/uwar_wiki/index.php?title=Matlab:Converting_Data Matlab:Converting_Data]) for use in feature extraction
 
** <tt>uwar_core_getGroundTruth</tt> - generates a <tt>groundTruth</tt> struct (see [http://embedded.seattle.intel-research.net/uwar_wiki/index.php?title=Matlab:Retrieving_Labels Matlab:Retrieving_Labels]) for supplying ground truth labels for training classifiers
 
  
These functions will be used throughout in the examples below:
+
    /projects/ubicomp/uwar/data/Tutorials
  
 
== Loading UWAR Sensor Data Into Matlab ==
 
== Loading UWAR Sensor Data Into Matlab ==
Line 40: Line 40:
 
Loading UWAR sensor data into structures that can be easily handled by Matlab is one of the key first steps before more complex operations can be performed.  Fortunately, the [[UWAR:Tools:IO]] input/output library makes it relatively easy to load UWAR data into Matlab.
 
Loading UWAR sensor data into structures that can be easily handled by Matlab is one of the key first steps before more complex operations can be performed.  Fortunately, the [[UWAR:Tools:IO]] input/output library makes it relatively easy to load UWAR data into Matlab.
  
Begin by starting up Matlab as described above.  Once Matlab is running, execute the following command:
+
Consider the tutorial trace <tt>Trace-ipaq.uwar</tt>.  We can list the sensors data available in the trace by calling:
 
 
    >> addpath('/projects/ubicomp/uwar/bin/matlab')
 
 
 
This will add key Matlab functions to your command path.  Let's use those functions to load some trace data.  Consider a trace file named <tt>Trace-walking.uwar</tt>.  We load it with the following command:
 
 
 
    >> mio = uwar_io_loadFile('Trace-walking.uwar');
 
 
 
The <tt>uwar_io_loadFile</tt> function should produce a [http://ubicomp.cs.washington.edu/uwar/apis/javadoc/com/intel/research/uwar/io/matlab/MatlabIO.html MatlabIO] object that can be used to inspect and load data from the specified trace.  We can see what sensors are available in the trace with the <tt>getSensorId()</tt> method.
 
 
 
    >> mio.getSensorIds()
 
 
    ans =
 
 
    java.lang.String[]:
 
 
        'annotation/1'
 
        'annotation/2'
 
        'annotation/3'
 
        'annotation/4'
 
        'annotation/5'
 
        'msb/amb_light'
 
        'msb/audio'
 
        'msb/bar_press'
 
        'msb/compass'
 
        'msb/hf_vis_light'
 
        'msb/humidity'
 
        'msb/ir_light'
 
        'msb/temp'
 
        'msb/temp_sensiron'
 
        'msb/vis_light'
 
        'msb/x_accel'
 
        'msb/y_accel'
 
        'msb/z_accel'
 
 
 
Our trace contains sensor data from the multi-sensor board (msb) and annotation fields which we know to be for the activities like walking and riding elevators.  The <tt>uwar_io_getData()</tt> function can be used to extract data from the trace.  To extract data for one sensor, you might make a call like:
 
 
    >> d = uwar_io_getData(mio,'msb/x_accel');
 
    >> d
 
   
 
    d =
 
   
 
        sensors: {'timestamp'  'msb/x_accel'}
 
        fields: {'timestamp'  'value'}
 
          data: {[286572x1 double]  [286572x1 double]}
 
 
 
The call to <tt>uwar_io_getData</tt> produces a struct with three fields:
 
 
 
* sensors
 
* fields
 
* data
 
 
 
Each field is a cell array with 1xN entries.  You can think of the struct as a data table for the specified sensor, organized into columns.  The <tt>sensors</tt> and <tt>fields</tt> combine to form a unique label for each column of data, while the <tt>data</tt> field holds that actual data colums themselves.
 
 
 
How many columns of data can we expect?  As said before, each field is 1xN, where N is the number of total data column in our table.  The first column is always the timeseries index, and the remaining columns are devoted to the specified sensors.  In this case, we only specified one sensor, 'msb/x_accel', which has one corresponding column of acceleration values.
 
 
 
We could easily plot acceleration versus time with the following command:
 
 
 
    % The first column is time and the second column is acceleration
 
    >> plot(d.data{1},d.data{2});
 
    >> xlabel(d.sensors{1});
 
    >> ylabel(d.sensors{2});
 
 
 
Now let's consider a more complicated example.  We typically don't care about just one accelerometer, but rather all three at once.  We can load multiple sensors at once with the following syntax:
 
 
 
    >> d = uwar_io_getData(mio,{'msb/x_accel','msb/y_accel','msb/z_accel'});
 
    >> d
 
   
 
    d =
 
   
 
    1x3 struct array with fields:
 
        sensors
 
        fields
 
        data
 
 
 
Notice now that instead of one struct, we were return an array of three structs: one for each accelerometer.  We can access each in turn by indexing the array:
 
  
     >> d(1)
+
     >> uwar_io_getSensorIds('Trace-ipaq.uwar')
 
      
 
      
 
     ans =  
 
     ans =  
 
      
 
      
         sensors: {'timestamp' 'msb/x_accel'}
+
         'wifi'
        fields: {'timestamp'  'value'}
+
        'latlon'
          data: {[286572x1 double]  [286572x1 double]}
+
        'clock'
  
Generally speaking, it's more efficient to load multiple sensors at once than to load each individually.
+
The <tt>uwar_io_getSensorIds</tt> function should produce a cell-array of sensor id names for data present in the trace.  A similar call reveals the sensors present in the tutorial trace <tt>Trace-msp.uwar</tt>:
  
In the above example, each of our three accelerometers is returned in its own struct with its own time indexThere are times, however, when we want data from multiple sensors to be returned with the same timeseries index so that we can directly compare values. Let's consider the following example, where we will load acceleration data and a 'walking' annotation with the same timeseries.
+
  >> uwar_io_getSensorIds('Trace-msp.uwar')
 +
   
 +
  ans =
 +
   
 +
      'msb/light_infrared'
 +
      'msb/bar_temp_press'
 +
      'MSPPowerLevels'
 +
      'MicroStrain3DMGX1'
 +
      'msb/sht_temp_humidity'
 +
      'msb/hf_vis_light'
 +
      'msb/acceleration'
 +
      'msb/light_visible'
  
    >> d = uwar_io_getData(mio,'annotation/1,msb/x_accel');
+
Once we have determined what data is available, we can retrieve the data itself with calls to <tt>uwar_io_getSensorData</tt>. Let's pull the 'wifi' data from the <tt>Trace-ipaq.uwar</tt> trace:
    >> d
 
   
 
    d =
 
   
 
        sensors: {'timestamp' 'annotation/1' 'msb/x_accel'}
 
        fields: {'timestamp'  'annotation'  'value'}
 
          data: {[286596x1 double]  [286596x1 double]  [286596x1 double]}
 
  
Notice the specification of the sensor ids: <tt>'annotation/1,msb/x_accel'</tt>.  Instead of specifying the ids as separate elements in an array, we lumped them together in one string, joined with a comma.  This instructs the data extraction routine that the specified sensors are to be joined on a common timeseries.  An arbitrary number of sensors can be joined in this fashion.
+
  >> r = uwar_io_getSensorData('Trace-ipaq.uwar','wifi')
 
+
   
The resulting data structure now has three colums: the timestamps, the annotation, and the accelerometer data.  We can plot all three together as follows:
+
  r =
 
+
   
    hold on;
+
        sensorId: 'wifi'
    plot(d.data{1},d.data{2},'r-');
+
      timestamps: [2518x1 double]
    plot(d.data{1},d.data{3},'b-');
+
          fields: {'bssid'  'rssi'}
    hold off;
+
             data: {{2518x1 cell} [2518x1 int32]}
 
 
We get a nice picture showing us that acceleration (blue) is a good indicator for walking (red):
 
 
 
[[Image:UWar_Tutorial_Matlab_Walking.png]]
 
 
 
== Audio Data ==
 
 
 
Audio data can be loaded with the same interfaces described above:
 
 
 
    >> mio = uwar_io_loadFile('Trace.uwar');
 
    >> d = uwar_io_getData('msb/audio');
 
    >> audio = d.data{2};
 
    >> audio = audio / max(abs(audio));
 
    >> audio = resample(audio,8192,15360);
 
    >> sound(audio);
 
 
 
The first three steps should be familiar: load the trace, extract the audio, and pull out the audio data column.  In the next step, we normalize the audio vector so that all values range from [-1,1].  The audio samples are stored in the UWAR file as 2-byte short values whose range is ~ +/- 32k. The normalization step puts them in range usable by Matlab.
 
 
 
The next step down-samples the audio from 15360 Hz to 8192 Hz.  You'll remember that the sensor board samples audio at 15360.  Due to a weird bug in Matlab, audio files only play right when they are at 8192 Hz (at least under Linux - note that the Fs argument to <tt>sound</tt> is silently ignored if specified).  If all goes well, some sort of sound should be coming out your speakers.
 
 
 
Users of Windows Matlab might also want to check out the <tt>play</tt> command.  You can also write audio data out to a WAV file with <tt>wavwrite</tt> command.
 
 
 
== Classification ==
 
 
 
There is a large body of Matlab code maintained by Jonathan Lester et. al. for doing activity classification from sensor board data. This code is documented extensively at the [http://embedded.seattle.intel-research.net/uwar_wiki/index.php?title=Main_Page Intel Research Seattle UWAR Wiki].
 
 
 
A version of this code has been prepared for use in CSE567 - Fall 05.  It can be found out in the filesystem at:
 
 
 
    /projects/ubicomp/uwar/dist/Matlab/Core
 
 
 
To learn more, check out the example code below, the IRS Wiki mentioned above, documentation for individual functions, and eventually the source code, depending on the level of detail you need.
 
 
 
=== Pre-Trained Classification ===
 
 
 
The following example executes that static and HMM traces on a particular UWAR trace and plots the results.  This traces assumes the existence of pre-trained classifiers, which would reside in the <tt>path/Classifiers</tt> directory.
 
 
 
<pre>
 
function [] = classifyTrace(traceFile,path)
 
 
 
if nargin == 1
 
    path = pwd();
 
end
 
 
 
% All the classes for which we have classifiers
 
allClasses.labels = { 'Sitting', 'Standing', 'Walking', 'Jogging', 'Up Stairs', 'Down Stairs', 'Bicycle', 'Car', 'Down Elevator', 'Up Elevator' };
 
allClasses.ids = [1:10];
 
 
 
% The annotated classes to test against
 
classes.labels =      { 'Walking',      'Up Stairs',    'Down Stairs',  'Up Elevator', 'Down Elevator'};
 
classes.annotations = { 'annotation/1', 'annotation/2', 'annotation/3', 'annotation/4', 'annotation/5' };
 
classes.ids =        [  3,              5,              6,             10,            9            ];
 
classDir = fullfile(path,'Classifiers');
 
 
 
% Check the base environment
 
uwar_io_init();
 
uwar_core_init();
 
global Core;
 
Core_CheckCore();
 
 
 
% Load the data
 
doLog('Loading trace...');
 
mio = uwar_io_loadFile(traceFile);
 
 
 
% Raw Data
 
doLog('Extracting sensor data from trace...');
 
completeData = uwar_core_getCompleteData(mio,path);
 
save( fullfile(path,'completeData.mat'), 'completeData');
 
   
 
% Ground Truth
 
doLog('Extracting ground truth labels from trace...');
 
groundTruth = uwar_core_getGroundTruth(mio,classes);
 
save(fullfile(path,'groundTruth.mat'),'groundTruth');
 
 
 
doLog('Extracting features...');
 
% Specify the frame length we'll use
 
Core.featureExtraction.frameLength        = 0.25;
 
% Specify the feature extraction function to use
 
Core.featureExtraction.extractionFunction = 'FS_9_calculateFeatures'; 
 
featureExtraction_computeFeatures( path, path, 0, 0 );
 
 
 
% Static Classification
 
doLog('Performing static classification...');
 
execute_StaticClassifier( path, path, classDir, allClasses.ids );
 
 
 
% HMM Classification
 
doLog('Performing HMMM classification...');
 
HMMWindowSize          = 60;
 
HMMadvancementSize    = 40;
 
execute_HMMClassifier( path, path, classDir, allClasses.ids, HMMWindowSize, HMMadvancementSize );
 
</pre>
 
 
 
<pre>
 
% Plot the Results
 
doLog('Plotting results...');
 
 
 
hold on;
 
title('Classification');
 
 
 
% Ground Truth
 
load(fullfile(path,'completeData.mat'));
 
load(fullfile(path,'groundTruth.mat'));
 
 
 
 
 
% We have to construct a vector of ground truth labels
 
% from the ground truth structure
 
start = completeData.ActualStart;
 
 
 
for vi=1:size(groundTruth.groundTruthScenes,2)
 
    actTimes = groundTruth.groundTruthScenes{vi};
 
    for vj=1:size(actTimes{1},2)
 
        actStart = floor((actTimes{1}(vj) - start)/0.25);
 
        actStop = ceil((actTimes{2}(vj) - start)/0.25);
 
        allActivites(1,actStart:actStop) = classes.ids(vi);
 
    end
 
end
 
 
 
% We bump up unlabeled data (value==0) to sitting (value==1)
 
% We also shift everything down 0.05 so it will be more
 
% visible in the final plot
 
allActivites = allActivites + (allActivites == 0) - 0.05;
 
 
 
% Create the time index for the ground truth data and plot
 
t=1:size(allActivites,2);
 
t=t*0.25;
 
plot(t,allActivites-0.1,'r.');
 
 
 
% Static Classifiers
 
load(fullfile(path,'staticClassifierOutput.mat'));
 
t = 0:size(staticClassifierResults,1)-1;
 
t = t * 0.25 + 60;
 
plot(t,staticClassifierResults,'g.');
 
 
 
% HMM Classifier
 
load(fullfile(path,'HMMClassifierOutput.mat'));
 
t = 0:size(HMMClassifierResults,1)-1;
 
t = t * 0.25 + 60;
 
plot(t,HMMClassifierResults,'b-');
 
 
 
% Add proper y-axis labels
 
yMin = min(allClasses.ids) - 1;
 
yMax = max(allClasses.ids) + 1;
 
set(gca,'YLim',[yMin,yMax]);
 
set(gca,'YTick',allClasses.ids);
 
set(gca,'YTickLabel',allClasses.labels);
 
 
 
legend('Ground Truth','Static Classifier','HMM Classifier');
 
ylabel('Class');
 
xlabel('Time (s)');
 
 
 
hold off;
 
</pre>
 
 
 
<pre>
 
% Support Functions
 
 
 
function [] = doLog( varargin )
 
%UNTITLED1 Summary of this function goes here
 
%  Detailed explanation goes here
 
  
if( length(varargin) > 1 )
+
Here we see the general form for all data returned by <tt>uwar_io_getSensorData</tt>.  The data is captured in a Matlab structure with the following fields:
    fprintf(1,strcat(varargin{1},'\n'),varargin{2:end});
 
else
 
    fprintf(1,strcat(varargin{1},'\n'));
 
end
 
  
function [b] = fileExists(file)
+
* sensorId - the name of the sensor for the data
b = (exist(file,'file') == 2);
+
* timestamps - a vector of timestamps for each row of data
</pre>
+
* fields - the name of the columns for each column of data for the sensor
 +
* data - the actual columns of data, whose individual types are determined by the type of field
  
=== Pre-Trained Classification Example: Walking Trace ===
+
You'll notice that the timestamp vector and each data column vector should have the same size.  While each vector may have the same size, we don't present them as one large matrix because the data types for each column may not support such a representation.  For example, the 'bssid' data for the 'wifi' sensor is a collection of BSSID strings, which can't go in a matrix, but rather a cell.
  
We ran the previous classifier on the 'Trace-walking.uwar' trace to produce the following output:
+
It may help to think of the above data structure as a table in a database.  The table has a name, which maps to the <tt>sensorId</tt> field.  The table will always have a <tt>timestamps</tt> column for representing the time index of each records in the table.  The table has additional columns, where each column has a name as represented by the <tt>fields</tt> field.  The data for the table is presented in a column-by-column representation as represented by the <tt>data</tt> field.
  
[[Image:UWar_Tutorial_Matlab_Activity_Classification.png]]
+
Also just like a database table, you can think of sensor having its own schema.  That is to say, the 'wifi' sensor will always have two fields 'bssid' and 'rssi'.  Each sensor will have its own schema, and while there is no formal documentation for this schema outside of the source code itself, the format of the data should be relatively straight-forward upon examination in Matlab.
  
 
== Other Matlab Resources ==
 
== Other Matlab Resources ==

Revision as of 22:48, 24 October 2006


Running Matlab

Matlab (7.0 R14) is available for departmental Linux machines in the /projects/matlab directory. You can run Matlab with the following command from your favorite shell:

   /projects/matlab/bin/matlab

If you are in an X11 environment, this will bring up a graphical front-end to Matlab. There is also a terminal-based interface available, but you will not be able to display plots and other visualizations in terminal-mode.

Running Matlab From Windows

If you are not lucky enough to have your own copy of Matlab for your Windows machine, you can still run departmental Matlab. You can always SSH into one of the cycle servers and run Matlab from the command line. Alternately, if you need full X11 support, you can do X11 forwarding to your Windows machine as described here:

http://www.cs.washington.edu/lab/remote-access/xclients.html

Adding UWAR Support

To add UWAR functions to Matlab, add the following path in Matlab:

   >> addpath('/projects/ubicomp/uwar/bin/matlab2');

Summary of UWAR Matlab Functions

All of the following Matlab functions should have internal help documentation, such that calling help function_name from within Matlab will produce useful documentation concerning functionality and calling conventions.

  • IO Functions:
    • uwar_io_init - Loads the UWAR Java IO library. Only call this directly if you are using a custom jar (that should be very few of you)
    • uwar_io_getSensorIds - List the available sensor data in a UWAR trace
    • uwar_io_getSensorData - Extract sensor data from a UWAR trace

Tutorial Data

Looking for UWAR Trace files to use in this tutorial? Check out files in:

   /projects/ubicomp/uwar/data/Tutorials

Loading UWAR Sensor Data Into Matlab

Loading UWAR sensor data into structures that can be easily handled by Matlab is one of the key first steps before more complex operations can be performed. Fortunately, the UWAR:Tools:IO input/output library makes it relatively easy to load UWAR data into Matlab.

Consider the tutorial trace Trace-ipaq.uwar. We can list the sensors data available in the trace by calling:

   >> uwar_io_getSensorIds('Trace-ipaq.uwar')
   
   ans = 
   
       'wifi'
       'latlon'
       'clock'

The uwar_io_getSensorIds function should produce a cell-array of sensor id names for data present in the trace. A similar call reveals the sensors present in the tutorial trace Trace-msp.uwar:

  >> uwar_io_getSensorIds('Trace-msp.uwar')

  ans =

      'msb/light_infrared'
      'msb/bar_temp_press'
      'MSPPowerLevels'
      'MicroStrain3DMGX1'
      'msb/sht_temp_humidity'
      'msb/hf_vis_light'
      'msb/acceleration'
      'msb/light_visible'

Once we have determined what data is available, we can retrieve the data itself with calls to uwar_io_getSensorData. Let's pull the 'wifi' data from the Trace-ipaq.uwar trace:

  >> r = uwar_io_getSensorData('Trace-ipaq.uwar','wifi')

  r =

        sensorId: 'wifi'
      timestamps: [2518x1 double]
          fields: {'bssid'  'rssi'}
            data: {{2518x1 cell}  [2518x1 int32]}

Here we see the general form for all data returned by uwar_io_getSensorData. The data is captured in a Matlab structure with the following fields:

  • sensorId - the name of the sensor for the data
  • timestamps - a vector of timestamps for each row of data
  • fields - the name of the columns for each column of data for the sensor
  • data - the actual columns of data, whose individual types are determined by the type of field

You'll notice that the timestamp vector and each data column vector should have the same size. While each vector may have the same size, we don't present them as one large matrix because the data types for each column may not support such a representation. For example, the 'bssid' data for the 'wifi' sensor is a collection of BSSID strings, which can't go in a matrix, but rather a cell.

It may help to think of the above data structure as a table in a database. The table has a name, which maps to the sensorId field. The table will always have a timestamps column for representing the time index of each records in the table. The table has additional columns, where each column has a name as represented by the fields field. The data for the table is presented in a column-by-column representation as represented by the data field.

Also just like a database table, you can think of sensor having its own schema. That is to say, the 'wifi' sensor will always have two fields 'bssid' and 'rssi'. Each sensor will have its own schema, and while there is no formal documentation for this schema outside of the source code itself, the format of the data should be relatively straight-forward upon examination in Matlab.

Other Matlab Resources