UWAR:Tutorial:Matlab

From PublicWiki
Revision as of 21:45, 9 October 2006 by Bdferris (talk | contribs)

Jump to: navigation, search


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

Tutorial Data

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

   /projects/ubicomp/uwar/data/Tutorials

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_loadFile - Load a UWAR trace file into memory for processing
    • uwar_io_getData - Extract sensor data from a loaded UWAR trace
  • Activity Recognition Functions:
    • uwar_core_getCompleteData - generates a completeData struct (see Matlab:Converting_Data) for use in feature extraction
    • uwar_core_getGroundTruth - generates a groundTruth struct (see Matlab:Retrieving_Labels) for supplying ground truth labels for training classifiers

These functions will be used throughout in the examples below:

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.

Begin by starting up Matlab as described above. Once Matlab is running, execute the following command:

   >> 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 Trace-walking.uwar. We load it with the following command:

   >> mio = uwar_io_loadFile('Trace-walking.uwar');

The uwar_io_loadFile function should produce a 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 getSensorId() 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 uwar_io_getData() 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 uwar_io_getData 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 sensors and fields combine to form a unique label for each column of data, while the data 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)
   
   ans = 
   
       sensors: {'timestamp'  'msb/x_accel'}
        fields: {'timestamp'  'value'}
          data: {[286572x1 double]  [286572x1 double]}

Generally speaking, it's more efficient to load multiple sensors at once than to load each individually.

In the above example, each of our three accelerometers is returned in its own struct with its own time index. There 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.

   >> d = uwar_io_getData(mio,'annotation/1,msb/x_accel');
   >> 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: 'annotation/1,msb/x_accel'. 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.

The resulting data structure now has three colums: the timestamps, the annotation, and the accelerometer data. We can plot all three together as follows:

   hold on;
   plot(d.data{1},d.data{2},'r-');
   plot(d.data{1},d.data{3},'b-');
   hold off;

We get a nice picture showing us that acceleration (blue) is a good indicator for walking (red):

File: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 sound 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 play command. You can also write audio data out to a WAV file with wavwrite 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 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 path/Classifiers directory.

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 );
% 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;
% Support Functions

function [] = doLog( varargin )
%UNTITLED1 Summary of this function goes here
%   Detailed explanation goes here

if( length(varargin) > 1 )
    fprintf(1,strcat(varargin{1},'\n'),varargin{2:end});
else
    fprintf(1,strcat(varargin{1},'\n'));
end

function [b] = fileExists(file)
b = (exist(file,'file') == 2);

Pre-Trained Classification Example: Walking Trace

We ran the previous classifier on the 'Trace-walking.uwar' trace to produce the following output:

File:UWar Tutorial Matlab Activity Classification.png

Other Matlab Resources