nofretete
nofretete

Reputation: 13

How can I fit my data better or shift my data? My fit is way below my data

We had to motion detect a pendulum by filming it,track the pendulum with a given software and store the t,x and y values in a txt-file. Since my hands were very shaky, the graph is very shaky (image attached). data and fit

This is my code:

import sys
sys.path.insert(0, "..")
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
%matplotlib inline
import pandas as pd



data = np.loadtxt('newdata.txt', delimiter = ',', skiprows = 1)

t = data[:,0]
x = data[:,1]

def cos_func(times, amplitude, frequency):
    return amplitude * np.cos(frequency * times)


def period2freq(period):
    return 1.0 / period * 2.0 * np.pi

def freq2period(freq):
    return 1.0 / freq * 2.0 * np.pi

popt, pcov = curve_fit(cos_func,  # our function
                       t,  # measured x values
                       x,  # measured y values
                       p0=(200, period2freq(150)))  # the initial guess for the two parameters


fig, ax = plt.subplots(1, 1)
ax.plot(t, x, label='Measured')
ax.plot(t, cos_func(t, popt[0], popt[1]), label='Best Fit')
ax.legend()

I tried something, this is my code now and the image, still bad:

# Laden der Daten
data = np.loadtxt('newdata.txt', delimiter = ',', skiprows = 1)
t = data[:,0]
x = data[:,1]

# Definition der Fit-Funktion mit Offset und Phase
def cos_func(times, amplitude, frequency, phase, offset):
    return amplitude * np.cos(frequency * times + phase) + offset

# Startparameter für den Fit
initial_amplitude = (np.max(x) - np.min(x)) / 2  # Amplitude als Hälfte der Amplitude der Daten
initial_frequency = 2 * np.pi / 150  # Annahme für die Frequenz
initial_phase = 0  # Startphase
initial_offset = -220  # Ihr Offset-Wert
shift = 10  # Verschiebung um 5 Einheiten nach rechts

# Durchführung des Fits
p0 = (initial_amplitude, initial_frequency, initial_phase, initial_offset)
popt, pcov = curve_fit(cos_func, t, x, p0=p0)

# Anpassen der Phase für die Verschiebung nach rechts
popt[2] += shift * initial_frequency

# Plotten der Daten und des Fits
plt.figure(figsize=(10, 6))
plt.plot(t, x, label='Measured')
plt.plot(t, cos_func(t, *popt), label='Best Fit')
plt.legend()
plt.xlabel('Time')
plt.ylabel('Position')
plt.title('Fit of Pendulum Motion')
plt.show()

new photo

I honestly just played around so it looked a bit better, but it's still bad and I don't understand it.

Upvotes: 1

Views: 68

Answers (2)

Max
Max

Reputation: 474

The function you are fitting has two properties that the data does not have:

  • It necessarily has its maximum at the beginning (t=0) since you use the cos function without a phase shift. You should definitely add a phase shift (additive constant within the argument)
  • Also, it necessarily oscillates around 0 because you don't allow for a shift in the y direction. You should also add this. (Since you allude to this in the question: yes, you could also shift down to a zero average value your data by simply subtracting the mean value from all data values. But adding an additive constant to the function used for the fit could be more versatile and would give a result more directly related to the original data.)

Summarizing you should use a function y(t) = a * cos(b * t + c) + d, with (a,b,c,d) to be determined.

PS: not only were you shaking very importantly, but also you seem to have followed the pendulum with the camera. As a consequence the position of the object on the image does not correspond to the absolute position of the object in space... But well, you'll still get a cosine curve that goes as well as possible through the data points....

Upvotes: 0

timz
timz

Reputation: 26

Have you tried changing your function to:

def cos_func(times, amplitude, frequency, offset):
   return amplitude * np.cos(frequency * times) + offset

Upvotes: 0

Related Questions