Skip to content

Commit 36fcd46

Browse files
danhaustsaoyu
authored andcommitted
Wave position (#255)
* initializing of queue and keeping constant number of elements after initialization ends * debug functions * copying queue after refresh period and fitting curve * sine changed to cosine, dubegging methods added * setup file for integrating wave_position with ROS * get_position method, added test cases, needs debugging * enable get_position only after initialization completes * docstring * cleaning up * integrating wave_position with ROS * removing time dependency during initialization * moving wave_position settings to ROS parameters * adjusting time_range for wave_position * debugging, reducing order of model function, adding bounds to curve_fit
1 parent a34e807 commit 36fcd46

File tree

3 files changed

+61
-26
lines changed

3 files changed

+61
-26
lines changed

src/sailing_robot/launch/parameters/default.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ procedure/timeout: 15 #in second
6565
procedure/exploration_coefficient: 0.1 #number between 0 and 1
6666

6767

68+
#
69+
# Wave Position
70+
#
71+
wave_position/time_range: 2.5 # time window in seconds captured by the wave_position algorithm
72+
wave_position/refresh_time: 0.5 # how often the model is re-trained
73+
6874

6975

7076
# Heading

src/sailing_robot/scripts/wave_position

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ from sailing_robot.wave_position import Wave_position
99
## Setting ##
1010
############################################################################
1111

12-
time_range = 3 # time window captured by the wave_position algorithm
13-
refresh_time = 0.5 # how often the model is re-trained
12+
time_range = rospy.get_param('wave_position/time_range') # time window captured by the wave_position algorithm
13+
refresh_time = rospy.get_param('wave_position/refresh_time') # how often the model is re-trained
1414

1515
"""
1616
The higher the time_range, the less the algorithm is sensitive to noise.

src/sailing_robot/src/sailing_robot/wave_position.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,28 @@
2929
import copy
3030
import numpy as np
3131
from scipy.optimize import curve_fit
32+
from scipy import __version__ as scipy_version
33+
3234

3335
class Wave_position():
3436
def __init__(self, frequency, time_range, refresh_time=1):
3537
# frequency: rate of acceleration reading
36-
# time range: readings aquired during this time window are used for predictions (idealy should be a few sea waves)
38+
# time range: readings acquired during this time window are used for predictions
3739
# refresh time: how often is prediction function re-trained on the most recent data
3840

41+
if ((int(scipy_version.split('.')[0]) == 0) and (int(scipy_version.split('.')[1]) < 17)):
42+
raise Exception("Your scipy is outdated. Minimal required version is 0.17.0. Your are currently running "+scipy_version)
43+
3944
self.period = 1.0/frequency # period in seconds between each two data points
4045
# queue is updated every time update func is called.
4146
self.time_range = time_range # time range captured (size of window for fitting the curve)
4247
self.refresh_time = refresh_time # after this time new fit is performed
4348
self.queue = deque()
49+
self.required_queue_length = frequency * time_range
4450
# xdata & ydata are updated in time_range periods
4551
self.xdata = np.array([])
4652
self.ydata = np.array([])
47-
self.popt = np.array([])
53+
self.popt = np.array([0, 0, 0])
4854
self.initializing = True
4955
self.last_refresh = 0 # time when last refresh occured
5056

@@ -53,32 +59,37 @@ def update(self, data_point):
5359

5460
self.queue.append(data_point)
5561
if (self.initializing):
56-
now = time.time()
57-
if (len(self.queue) <= 1):
58-
self.last_refresh = now
59-
if ((now - self.last_refresh) >= self.time_range):
62+
if (len(self.queue) >= self.required_queue_length):
6063
self.initializing = False
6164
else:
6265
# Delete the oldest value to keep the queue size constant.
6366
self.queue.popleft()
6467
now = time.time()
6568
if ((now - self.last_refresh) >= self.refresh_time):
66-
self.last_refresh = now
67-
# Copy current queue to self.ydata.
68-
self.process_queue()
69-
# Fit model_func to the data.
70-
self.train()
71-
72-
73-
def model_func(self, x, a, b, c, d):
74-
return a * (np.cos(b * x + c)) + d
69+
try:
70+
# Copy current queue to self.ydata.
71+
self.process_queue()
72+
# Fit model_func to the data.
73+
self.train()
74+
self.last_refresh = now
75+
except RuntimeError as e:
76+
print(e)
77+
78+
def model_func(self, x, a, b, c):
79+
return a * (np.cos(b * x + c)) + np.mean(self.ydata)
7580

7681
def train(self):
7782
"""
7883
Fits model_func to self.xdata & self.ydata and saves fit parameters
7984
to self.popt.
8085
"""
81-
self.popt, pcov = curve_fit(self.model_func, self.xdata, self.ydata)
86+
guess_freq = 0.5
87+
guess_amplitude = 3*np.std(self.ydata)/(2**0.5)
88+
guess_phase = 0
89+
p0=[guess_amplitude, guess_freq, guess_phase]
90+
91+
popt, pcov = curve_fit(f=self.model_func, xdata=self.xdata, ydata=self.ydata, p0=p0, bounds=((0, 0, 0), (100., 2., np.pi)), maxfev=2000)
92+
self.popt = popt
8293

8394
def process_queue(self):
8495
"""
@@ -100,12 +111,21 @@ def get_position(self):
100111
(It is relative distance from the last crest.)
101112
"""
102113
if (not self.initializing):
103-
a, b, c, d = self.popt
104114
pi = np.pi
115+
amplitude, frequency, phase = self.popt
116+
last_refresh = self.last_refresh
105117
now = time.time()
106-
diff = float(now - self.refresh_time)
107-
cos_inner = float(b * diff + c)
118+
diff = float(now - last_refresh)
119+
cos_inner = float(frequency * diff + phase)
108120
rel_distance = cos_inner/pi - int(cos_inner/pi)
121+
122+
# print("frequency: {}".format(frequency))
123+
# print("diff: {}".format(diff))
124+
# print("phase: {}".format(phase))
125+
# print("cos_inner: {}".format(cos_inner))
126+
# print("rel_distance: {}".format(rel_distance))
127+
# print("\n")
128+
109129
return rel_distance
110130
else:
111131
return "initializing"
@@ -121,17 +141,26 @@ def get_position(self):
121141
# np.random.seed(1729)
122142
# y_noise = 0.2 * np.random.normal(size=self.xdata.size)
123143
# self.ydata = y + y_noise
124-
#
125-
# def plot(self):
144+
145+
# def plot_all(self):
146+
# import matplotlib.pyplot as plt
147+
# plt.plot(self.xdata, self.ydata, 'b*', label='training data')
148+
# plt.plot(self.xdata, self.model_func(self.xdata, *self.popt), 'r+', label='predicted')
149+
# plt.legend(loc=0)
150+
# plt.show()
151+
152+
# def plot_training_data(self):
126153
# import matplotlib.pyplot as plt
127-
# plt.plot(self.xdata, self.ydata, 'b*', label='data')
128-
# plt.plot(self.xdata, self.model_func(self.xdata, *self.popt), 'r+')
154+
# plt.plot(self.xdata, self.ydata, 'b*')
129155
# plt.show()
130-
#
156+
131157
# def print_vars(self):
132158
# print("period: {}\n".format(self.period))
133159
# print("time_range: {}\n".format(self.time_range))
134160
# print("refresh_time: {}\n".format(self.refresh_time))
161+
# print("queue size: {}\n".format(len(self.queue)))
162+
# print("xdata size: {}\n".format(len(self.xdata)))
163+
# print("ydata size: {}\n".format(len(self.ydata)))
135164
# print("queue: {}\n".format(self.queue))
136165
# print("xdata: {}\n".format(self.xdata))
137166
# print("ydata: {}\n".format(self.ydata))

0 commit comments

Comments
 (0)