Skip to content

Commit e8a1bc1

Browse files
committedApr 29, 2011
Initial commit of code implementing the FOAW velocity estimation technique.
0 parents  commit e8a1bc1

File tree

4 files changed

+354
-0
lines changed

4 files changed

+354
-0
lines changed
 

‎COPYING

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Copyright (c) 2008, Stephen Sinclair
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are
6+
met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above
12+
copyright notice, this list of conditions and the following
13+
disclaimer in the documentation and/or other materials provided
14+
with the distribution.
15+
16+
* Neither the name of McGill University, nor the Input Devices and
17+
Music Interaction Lab, nor the names of its contributors may be
18+
used to endorse or promote products derived from this software
19+
without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

‎Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
foaw: foaw.o
3+
4+
run: foaw
5+
./foaw | ./plot.py

‎foaw.c

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <math.h>
5+
#include <string.h>
6+
7+
/*
8+
* Perform the FOAW velocity estimation routine.
9+
* This algorithm is described here:
10+
*
11+
* Janabi-Sharifi, F.; Hayward, V.; Chen, C.-S.J., "Discrete-time
12+
* adaptive windowing for velocity estimation," Control Systems
13+
* Technology, IEEE Transactions on , vol.8, no.6, pp.1003-1009, Nov
14+
* 2000
15+
*
16+
* http://www.cim.mcgill.ca/~haptic/pub/FS-VH-CSC-TCST-00.pdf
17+
*
18+
* This implementation (C)2008 Stephen Sinclair, IDMIL, McGill
19+
* University. This work is covered by the GPL-compatible version of
20+
* the BSD license, please see the following URL for more information:
21+
*
22+
* http://www.opensource.org/licenses/bsd-license.html
23+
*
24+
* The exact license is listed in the file COPYING, which you should
25+
* have received with this source code.
26+
*/
27+
28+
#define SR 1000
29+
#define T (1.0f/SR)
30+
#define SIZE 1000
31+
#define NOISE (0.1*T)
32+
33+
float vel[SIZE];
34+
float pos[SIZE];
35+
float mpos[SIZE];
36+
float fdvel[SIZE];
37+
float foawvel[SIZE];
38+
float dif[SIZE];
39+
40+
#ifndef min
41+
#define min(X,Y) ((X) < (Y) ? (X) : (Y))
42+
#endif
43+
44+
void generate_velocity()
45+
{
46+
int k;
47+
for (k=0; k<50; k++)
48+
vel[k] = 0;
49+
for (; k<150; k++)
50+
vel[k] = 1;
51+
for (; k<450; k++)
52+
vel[k] = 2;
53+
for (; k<600; k++)
54+
vel[k] = 0;
55+
for (; k<700; k++)
56+
vel[k] = -1;
57+
for (; k<1000; k++)
58+
vel[k] = -2;
59+
}
60+
61+
void integrate_position()
62+
{
63+
int k;
64+
pos[0] = 0;
65+
for (k=1; k<SIZE; k++)
66+
pos[k] = (vel[k]*T+pos[k-1]);
67+
}
68+
69+
void add_position_noise()
70+
{
71+
int k;
72+
for (k=0; k<SIZE; k++)
73+
pos[k] += rand()/(float)RAND_MAX * NOISE;
74+
}
75+
76+
void finite_difference()
77+
{
78+
int k;
79+
fdvel[0] = 0;
80+
for (k=1; k<SIZE; k++)
81+
fdvel[k] = (pos[k]-pos[k-1])/T;
82+
}
83+
84+
float do_foaw_sample(float *posbuf, int size, int *k,
85+
float current_pos, int best)
86+
{
87+
int i, j, l, bad;
88+
float b, ykj;
89+
float velocity = 0;
90+
float noise_max = NOISE;
91+
92+
/* circular buffer */
93+
*k = (*k+1)%size;
94+
posbuf[*k] = current_pos;
95+
96+
for (i=1; i<size; i++)
97+
{
98+
if (best)
99+
{
100+
// best-fit-FOAW
101+
b = 0;
102+
for (l=0; l<(i+1); l++)
103+
b += i*posbuf[(*k-l+size)%size]
104+
- 2*posbuf[(*k-l+size)%size]*l;
105+
b = b / (T*i*(i+1)*(i+2)/6);
106+
}
107+
else
108+
// end-fit-FOAW
109+
b = (posbuf[*k]-posbuf[(*k-i+size)%size]) / (i*T);
110+
bad = 0;
111+
for (j=1; j<i; j++)
112+
{
113+
ykj = posbuf[*k]-(b*j*T);
114+
if ( (ykj < (posbuf[(*k-j+size)%size]-noise_max))
115+
|| (ykj > (posbuf[(*k-j+size)%size]+noise_max)))
116+
{
117+
bad = 1;
118+
break;
119+
}
120+
}
121+
if (bad) break;
122+
velocity = b;
123+
}
124+
125+
return velocity;
126+
}
127+
128+
void do_foaw(float *pos, int n, int best)
129+
{
130+
float *posbuf = malloc(sizeof(float)*n);
131+
int k, i=0;
132+
memset(posbuf, 0, sizeof(float)*n);
133+
for (k=0; k<SIZE; k++)
134+
foawvel[k] = do_foaw_sample(posbuf, n, &i, pos[k], best);
135+
free(posbuf);
136+
}
137+
138+
void subtract(float *result, float *from, float *what)
139+
{
140+
int k;
141+
for (k=0; k<SIZE; k++)
142+
result[k] = from[k]-what[k];
143+
}
144+
145+
void median_position(int n)
146+
{
147+
int k, i, j, off=600;
148+
float *buf;
149+
buf = malloc(sizeof(float)*2*n);
150+
for (k=0; k<SIZE; k++)
151+
{
152+
int size = min(n,k);
153+
int a=n, b=n;
154+
buf[n] = pos[k];
155+
for (i=1; i<size; i++)
156+
{
157+
if (pos[k-i] > buf[b]) {
158+
buf[b+1] = pos[k-i];
159+
b++;
160+
}
161+
else if (pos[k-i] < buf[a]) {
162+
buf[a-1] = pos[k-i];
163+
a--;
164+
}
165+
else if (pos[k-i] > buf[b-1]) {
166+
buf[b+1] = buf[b];
167+
buf[b] = pos[k-i];
168+
b++;
169+
}
170+
else if (pos[k-i] < buf[a+1]) {
171+
buf[a-1] = buf[a];
172+
buf[a] = pos[k-i];
173+
a--;
174+
}
175+
}
176+
mpos[k] = buf[(b-a)/2+a];
177+
}
178+
free(buf);
179+
}
180+
181+
void dump(float *data)
182+
{
183+
int k;
184+
for (k=0; k<SIZE; k++)
185+
printf("%f\n", data[k]);
186+
}
187+
188+
int main()
189+
{
190+
generate_velocity();
191+
integrate_position();
192+
add_position_noise();
193+
finite_difference();
194+
median_position(3);
195+
do_foaw(mpos, 15, 1);
196+
#if 1
197+
dump(foawvel);
198+
#else
199+
subtract(dif, foawvel, vel);
200+
dump(dif);
201+
#endif
202+
return 0;
203+
}

‎velocity.py

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env python
2+
3+
from pylab import *
4+
from scipy.signal import lfilter, butter
5+
6+
# Constants
7+
sr = 1000.0;
8+
T = 1/sr;
9+
r = int(sr/100);
10+
noise_max = 0.1*T; # This is ||e_k||inf
11+
12+
# Define a velocity curve
13+
vel = array([0.]*(15*r) + [1.]*(4*r) + [2.]*(25*r) + [0.]*(5*r)
14+
+ [-1.]*(3*r) + [-1.]*(20*r))
15+
time = arange(len(vel))/float(sr);
16+
17+
# Integrate it to get position
18+
pos = lfilter([1], [1,-1], vel)*T;
19+
20+
# Add some noise
21+
pos = pos + rand(len(pos))*noise_max
22+
23+
# Finite difference
24+
fdvel = lfilter([1,-1],[1],pos)/T
25+
26+
# Butterworth 100 Hz
27+
[B,A] = butter(1, 0.1)
28+
bwvel = lfilter(B,A,fdvel)
29+
30+
# FD skip 3
31+
dist = 3
32+
fd3vel = lfilter(array([1]+[0]*(dist-1)+[-1])/float(dist),[1],pos)/T
33+
34+
# Least squared 15 (from Freedom6S API)
35+
def leastsquared(n=15):
36+
dTemp = (n - 1) / 2.0;
37+
dTemp2 = 12.0/ (n * ((n*n) - 1));
38+
return (dTemp-arange(n))*dTemp2
39+
40+
lsvel = lfilter(leastsquared(15), 1, pos)/T
41+
42+
# First-Order Adaptive Windowing (FOAW)
43+
def foaw(pos, n=16, best=False):
44+
result = zeros(len(pos))
45+
for k in range(len(pos)):
46+
velocity = 0
47+
for i in range(1,min(n,k)):
48+
# Calculate slope over interval
49+
if (best):
50+
# least squared method (best-fit-FOAW)
51+
b = ( ( i*sum([pos[k-j] for j in range(i+1)])
52+
- 2*sum([pos[k-j]*j for j in range(i+1)]) )
53+
/ (T*i*(i+1)*(i+2)/6) )
54+
else:
55+
# direct method (end-fit-FOAW)
56+
b = (pos[k]-pos[k-i]) / (i*T)
57+
58+
# Check the linear estimate of each middle point
59+
outside = False
60+
for j in range(1,i):
61+
ykj = pos[k]-(b*j*T)
62+
63+
# Compare to the measured value within the noise margin
64+
# If it's outside noise margin, return last estimate
65+
if ykj < (pos[k-j]-noise_max) or ykj > (pos[k-j]+noise_max):
66+
outside = True
67+
break
68+
if outside: break
69+
velocity = b
70+
71+
result[k] = velocity
72+
73+
return result
74+
75+
def median_filter(pos, n=5):
76+
result = zeros(len(pos))
77+
for k in range(1,len(pos)):
78+
result[k] = median(pos[k-min(n,k):k])
79+
return result
80+
81+
endfitfoawvel = foaw(pos, n=16, best=False)
82+
bestfitfoawvel = foaw(pos, n=16, best=True)
83+
mpos = median_filter(pos, n=3)
84+
endfitfoawvelm = foaw(mpos, n=16, best=False)
85+
bestfitfoawvelm = foaw(mpos, n=16, best=True)
86+
87+
# Plotting, velocity curves and derivatives
88+
def plotcurves(curves, titles, vel_yrange=None, dif_yrange=None):
89+
for n, v in enumerate(curves):
90+
acc = v-vel
91+
subplot(len(curves),2,n*2+1)
92+
plot(time, v)
93+
if (vel_yrange!=None):
94+
axis([time[0],time[-1],vel_yrange[0],vel_yrange[1]])
95+
title(titles[n]+': velocity')
96+
subplot(len(curves),2,n*2+2)
97+
plot(time, acc)
98+
if (dif_yrange!=None):
99+
axis([time[0],time[-1],dif_yrange[0],dif_yrange[1]])
100+
title(titles[n]+': ideal difference')
101+
show()
102+
103+
curves = [fdvel, fd3vel, bwvel, lsvel]
104+
titles = ['Simple Finite Difference',
105+
'Finite difference 3',
106+
'Butterworth %d Hz'%(sr*0.1),
107+
'Least Squared']
108+
109+
plotcurves(curves, titles, vel_yrange = [-1.5, 2.5], dif_yrange = [-0.3, 0.3])
110+
111+
curves = [endfitfoawvel,bestfitfoawvel,endfitfoawvelm,bestfitfoawvelm]
112+
titles = ['end-fit-FOAW','best-fit-FOAW','end-fit-FOAW w/ median','best-fit-FOAW w/ median']
113+
114+
plotcurves(curves, titles, vel_yrange = [-1.5, 2.5], dif_yrange = [-0.3, 0.3])
115+

0 commit comments

Comments
 (0)
Please sign in to comment.