modified: __pycache__/evaluation_studio.cpython-314.pyc
modified: evaluation_studio.py modified: scripts/dataset.py new file: scripts/inference_utils.py modified: scripts/predict_single.py modified: scripts_2/__pycache__/task3_identify.cpython-314.pyc modified: scripts_2/task3_identify.py
This commit is contained in:
Binary file not shown.
@@ -74,6 +74,109 @@ def build_envelope_curve(
|
||||
return time_values, envelope
|
||||
|
||||
|
||||
def log_decrement_metrics(peak_amplitudes: np.ndarray) -> tuple[float, float, float]:
|
||||
amplitudes = np.asarray(peak_amplitudes, dtype=np.float64).reshape(-1)
|
||||
if amplitudes.size < 2 or np.any(amplitudes <= 0.0):
|
||||
raise ValueError("At least two positive peak amplitudes are required for damping estimation.")
|
||||
log_decrements = np.log(amplitudes[:-1] / amplitudes[1:])
|
||||
mean_log_decrement = float(np.mean(log_decrements))
|
||||
damping_ratio = float(mean_log_decrement / (2.0 * np.pi))
|
||||
damping_ratio_exact = float(
|
||||
mean_log_decrement / np.sqrt((2.0 * np.pi) ** 2 + mean_log_decrement**2)
|
||||
)
|
||||
return mean_log_decrement, damping_ratio, damping_ratio_exact
|
||||
|
||||
|
||||
def estimate_global_decay_metrics(
|
||||
filtered_signal: np.ndarray,
|
||||
peak_indices: np.ndarray,
|
||||
sampling_rate: float,
|
||||
min_peaks: int = 5,
|
||||
) -> dict[str, np.ndarray | float]:
|
||||
filtered_signal = np.asarray(filtered_signal, dtype=np.float64).reshape(-1)
|
||||
peak_indices = np.asarray(peak_indices, dtype=int).reshape(-1)
|
||||
if peak_indices.size < min_peaks:
|
||||
raise ValueError("Too few peaks were detected for global damping estimation.")
|
||||
|
||||
amplitudes = np.abs(filtered_signal[peak_indices])
|
||||
tail_start = int(filtered_signal.size * 0.85)
|
||||
noise_slice = filtered_signal[tail_start:] if tail_start < filtered_signal.size else filtered_signal
|
||||
noise_floor = max(float(np.median(np.abs(noise_slice))), 1e-8)
|
||||
useful_mask = amplitudes > max(3.0 * noise_floor, 0.05 * float(amplitudes.max()))
|
||||
useful_indices = peak_indices[useful_mask]
|
||||
useful_amplitudes = amplitudes[useful_mask]
|
||||
if useful_indices.size < min_peaks:
|
||||
useful_indices = peak_indices
|
||||
useful_amplitudes = amplitudes
|
||||
|
||||
time_window = useful_indices.astype(np.float64) / sampling_rate
|
||||
log_amp = np.log(np.maximum(useful_amplitudes, 1e-12))
|
||||
slope, intercept = np.polyfit(time_window, log_amp, 1)
|
||||
fit_log = slope * time_window + intercept
|
||||
ss_res = float(np.sum((log_amp - fit_log) ** 2))
|
||||
ss_tot = float(np.sum((log_amp - np.mean(log_amp)) ** 2))
|
||||
r_squared = 1.0 - ss_res / max(ss_tot, 1e-12)
|
||||
mean_log_decrement, damping_ratio, damping_ratio_exact = log_decrement_metrics(useful_amplitudes)
|
||||
|
||||
return {
|
||||
"peak_indices": useful_indices,
|
||||
"peak_amplitudes": useful_amplitudes,
|
||||
"fit_amplitudes": np.exp(fit_log),
|
||||
"r_squared": float(r_squared),
|
||||
"mean_log_decrement": float(mean_log_decrement),
|
||||
"damping_ratio": float(damping_ratio),
|
||||
"damping_ratio_exact": float(damping_ratio_exact),
|
||||
}
|
||||
|
||||
|
||||
def estimate_tail_from_max_peak_metrics(
|
||||
filtered_signal: np.ndarray,
|
||||
peak_indices: np.ndarray,
|
||||
sampling_rate: float,
|
||||
min_peaks: int = 5,
|
||||
) -> dict[str, np.ndarray | float]:
|
||||
filtered_signal = np.asarray(filtered_signal, dtype=np.float64).reshape(-1)
|
||||
peak_indices = np.asarray(peak_indices, dtype=int).reshape(-1)
|
||||
if peak_indices.size < min_peaks:
|
||||
raise ValueError("Too few peaks were detected for max-peak-tail damping estimation.")
|
||||
|
||||
amplitudes = np.abs(filtered_signal[peak_indices])
|
||||
max_peak_pos = int(np.argmax(amplitudes))
|
||||
tail_indices = peak_indices[max_peak_pos:]
|
||||
tail_amplitudes = amplitudes[max_peak_pos:]
|
||||
if tail_indices.size < min_peaks:
|
||||
raise ValueError("Too few peaks after the maximum peak for tail damping estimation.")
|
||||
|
||||
tail_start = int(filtered_signal.size * 0.85)
|
||||
noise_slice = filtered_signal[tail_start:] if tail_start < filtered_signal.size else filtered_signal
|
||||
noise_floor = max(float(np.median(np.abs(noise_slice))), 1e-8)
|
||||
useful_mask = tail_amplitudes > max(3.0 * noise_floor, 0.05 * float(tail_amplitudes.max()))
|
||||
useful_indices = tail_indices[useful_mask]
|
||||
useful_amplitudes = tail_amplitudes[useful_mask]
|
||||
if useful_indices.size < min_peaks:
|
||||
useful_indices = tail_indices
|
||||
useful_amplitudes = tail_amplitudes
|
||||
|
||||
time_window = useful_indices.astype(np.float64) / sampling_rate
|
||||
log_amp = np.log(np.maximum(useful_amplitudes, 1e-12))
|
||||
slope, intercept = np.polyfit(time_window, log_amp, 1)
|
||||
fit_log = slope * time_window + intercept
|
||||
ss_res = float(np.sum((log_amp - fit_log) ** 2))
|
||||
ss_tot = float(np.sum((log_amp - np.mean(log_amp)) ** 2))
|
||||
r_squared = 1.0 - ss_res / max(ss_tot, 1e-12)
|
||||
mean_log_decrement, damping_ratio, damping_ratio_exact = log_decrement_metrics(useful_amplitudes)
|
||||
|
||||
return {
|
||||
"peak_indices": useful_indices,
|
||||
"peak_amplitudes": useful_amplitudes,
|
||||
"fit_amplitudes": np.exp(fit_log),
|
||||
"r_squared": float(r_squared),
|
||||
"mean_log_decrement": float(mean_log_decrement),
|
||||
"damping_ratio": float(damping_ratio),
|
||||
"damping_ratio_exact": float(damping_ratio_exact),
|
||||
}
|
||||
|
||||
|
||||
def save_task3_figure(
|
||||
file_path: Path,
|
||||
output_dir: Path,
|
||||
@@ -85,7 +188,15 @@ def save_task3_figure(
|
||||
selected_peak_amplitudes: np.ndarray,
|
||||
fit_amplitudes: np.ndarray,
|
||||
natural_frequency_hz: float,
|
||||
damping_ratio: float,
|
||||
local_damping_ratio: float,
|
||||
global_peak_indices: np.ndarray,
|
||||
global_peak_amplitudes: np.ndarray,
|
||||
global_fit_amplitudes: np.ndarray,
|
||||
global_damping_ratio: float,
|
||||
tail_peak_indices: np.ndarray,
|
||||
tail_peak_amplitudes: np.ndarray,
|
||||
tail_fit_amplitudes: np.ndarray,
|
||||
tail_damping_ratio: float,
|
||||
) -> Path:
|
||||
output_dir = ensure_parent_dir(output_dir)
|
||||
peak_times = time_values[selected_peak_indices]
|
||||
@@ -111,7 +222,18 @@ def save_task3_figure(
|
||||
axes[2].scatter(peak_times, selected_peak_amplitudes, color="tab:red", s=28, label="Selected envelope peaks")
|
||||
axes[2].plot(envelope_time, envelope, color="tab:green", linewidth=1.8, label="Fitted positive envelope")
|
||||
axes[2].plot(envelope_time, -envelope, color="tab:green", linewidth=1.2, linestyle="--", label="Fitted negative envelope")
|
||||
axes[2].set_title(f"Selected decay segment | damping ratio zeta = {damping_ratio:.5f}")
|
||||
global_peak_times = time_values[global_peak_indices]
|
||||
axes[2].scatter(global_peak_times, global_peak_amplitudes, color="tab:purple", s=14, alpha=0.65, label="Global peaks")
|
||||
axes[2].plot(global_peak_times, global_fit_amplitudes, color="tab:purple", linewidth=1.2, linestyle="-.", label="Global envelope fit")
|
||||
tail_peak_times = time_values[tail_peak_indices]
|
||||
axes[2].scatter(tail_peak_times, tail_peak_amplitudes, color="tab:brown", s=14, alpha=0.65, label="Max-peak tail peaks")
|
||||
axes[2].plot(tail_peak_times, tail_fit_amplitudes, color="tab:brown", linewidth=1.2, linestyle=":", label="Max-peak tail fit")
|
||||
axes[2].set_title(
|
||||
"Damping ratio | "
|
||||
f"local zeta={local_damping_ratio:.5f} | "
|
||||
f"global zeta={global_damping_ratio:.5f} | "
|
||||
f"max-tail zeta={tail_damping_ratio:.5f}"
|
||||
)
|
||||
axes[2].set_xlabel("Time (s)")
|
||||
axes[2].set_ylabel("Acceleration")
|
||||
axes[2].grid(True, alpha=0.25)
|
||||
@@ -154,11 +276,16 @@ def analyze_free_vibration(
|
||||
selected_peak_indices = np.asarray(peak_window["peak_indices"], dtype=int)
|
||||
selected_peak_amplitudes = np.asarray(peak_window["peak_amplitudes"], dtype=np.float64)
|
||||
fit_amplitudes = np.asarray(peak_window["fit_amplitudes"], dtype=np.float64)
|
||||
log_decrements = np.log(selected_peak_amplitudes[:-1] / selected_peak_amplitudes[1:])
|
||||
mean_log_decrement = float(np.mean(log_decrements))
|
||||
damping_ratio = float(mean_log_decrement / (2.0 * np.pi))
|
||||
damping_ratio_exact = float(
|
||||
mean_log_decrement / np.sqrt((2.0 * np.pi) ** 2 + mean_log_decrement**2)
|
||||
mean_log_decrement, damping_ratio, damping_ratio_exact = log_decrement_metrics(selected_peak_amplitudes)
|
||||
global_metrics = estimate_global_decay_metrics(
|
||||
filtered_signal=filtered_signal,
|
||||
peak_indices=peak_indices,
|
||||
sampling_rate=sampling_rate,
|
||||
)
|
||||
tail_metrics = estimate_tail_from_max_peak_metrics(
|
||||
filtered_signal=filtered_signal,
|
||||
peak_indices=peak_indices,
|
||||
sampling_rate=sampling_rate,
|
||||
)
|
||||
|
||||
figure_path = save_task3_figure(
|
||||
@@ -172,7 +299,15 @@ def analyze_free_vibration(
|
||||
selected_peak_amplitudes=selected_peak_amplitudes,
|
||||
fit_amplitudes=fit_amplitudes,
|
||||
natural_frequency_hz=natural_frequency_hz,
|
||||
damping_ratio=damping_ratio,
|
||||
local_damping_ratio=damping_ratio,
|
||||
global_peak_indices=np.asarray(global_metrics["peak_indices"], dtype=int),
|
||||
global_peak_amplitudes=np.asarray(global_metrics["peak_amplitudes"], dtype=np.float64),
|
||||
global_fit_amplitudes=np.asarray(global_metrics["fit_amplitudes"], dtype=np.float64),
|
||||
global_damping_ratio=float(global_metrics["damping_ratio"]),
|
||||
tail_peak_indices=np.asarray(tail_metrics["peak_indices"], dtype=int),
|
||||
tail_peak_amplitudes=np.asarray(tail_metrics["peak_amplitudes"], dtype=np.float64),
|
||||
tail_fit_amplitudes=np.asarray(tail_metrics["fit_amplitudes"], dtype=np.float64),
|
||||
tail_damping_ratio=float(tail_metrics["damping_ratio"]),
|
||||
)
|
||||
if show:
|
||||
plt.show()
|
||||
@@ -191,6 +326,16 @@ def analyze_free_vibration(
|
||||
"mean_log_decrement": mean_log_decrement,
|
||||
"damping_ratio": damping_ratio,
|
||||
"damping_ratio_exact": damping_ratio_exact,
|
||||
"global_selected_peak_count": int(len(np.asarray(global_metrics["peak_indices"], dtype=int))),
|
||||
"global_mean_log_decrement": float(global_metrics["mean_log_decrement"]),
|
||||
"global_damping_ratio": float(global_metrics["damping_ratio"]),
|
||||
"global_damping_ratio_exact": float(global_metrics["damping_ratio_exact"]),
|
||||
"global_envelope_r2": float(global_metrics["r_squared"]),
|
||||
"max_tail_selected_peak_count": int(len(np.asarray(tail_metrics["peak_indices"], dtype=int))),
|
||||
"max_tail_mean_log_decrement": float(tail_metrics["mean_log_decrement"]),
|
||||
"max_tail_damping_ratio": float(tail_metrics["damping_ratio"]),
|
||||
"max_tail_damping_ratio_exact": float(tail_metrics["damping_ratio_exact"]),
|
||||
"max_tail_envelope_r2": float(tail_metrics["r_squared"]),
|
||||
"envelope_r2": float(peak_window["r_squared"]),
|
||||
"figure_path": str(figure_path),
|
||||
}
|
||||
@@ -202,12 +347,22 @@ def print_report(result: dict[str, float | int | str | Path]) -> None:
|
||||
print(f"Top response sensor: {result['sensor_code']} / {result['axis']}")
|
||||
print(f"Sampling rate: {result['sampling_rate_hz']:.4f} Hz")
|
||||
print(f"Natural frequency f_n: {result['natural_frequency_hz']:.6f} Hz")
|
||||
print(f"Mean log decrement delta: {result['mean_log_decrement']:.6f}")
|
||||
print(f"Damping ratio zeta (delta / 2pi): {result['damping_ratio']:.6f}")
|
||||
print(f"Damping ratio exact: {result['damping_ratio_exact']:.6f}")
|
||||
print(f"Local mean log decrement delta: {result['mean_log_decrement']:.6f}")
|
||||
print(f"Local damping ratio zeta (delta / 2pi): {result['damping_ratio']:.6f}")
|
||||
print(f"Local damping ratio exact: {result['damping_ratio_exact']:.6f}")
|
||||
print(f"Global mean log decrement delta: {result['global_mean_log_decrement']:.6f}")
|
||||
print(f"Global damping ratio zeta (delta / 2pi): {result['global_damping_ratio']:.6f}")
|
||||
print(f"Global damping ratio exact: {result['global_damping_ratio_exact']:.6f}")
|
||||
print(f"Max-tail mean log decrement delta: {result['max_tail_mean_log_decrement']:.6f}")
|
||||
print(f"Max-tail damping ratio zeta (delta / 2pi): {result['max_tail_damping_ratio']:.6f}")
|
||||
print(f"Max-tail damping ratio exact: {result['max_tail_damping_ratio_exact']:.6f}")
|
||||
print(f"Detected peaks: {result['detected_peak_count']}")
|
||||
print(f"Selected peaks for envelope: {result['selected_peak_count']}")
|
||||
print(f"Envelope linearity R^2: {result['envelope_r2']:.6f}")
|
||||
print(f"Selected peaks for local envelope: {result['selected_peak_count']}")
|
||||
print(f"Selected peaks for global envelope: {result['global_selected_peak_count']}")
|
||||
print(f"Selected peaks for max-tail envelope: {result['max_tail_selected_peak_count']}")
|
||||
print(f"Local envelope linearity R^2: {result['envelope_r2']:.6f}")
|
||||
print(f"Global envelope linearity R^2: {result['global_envelope_r2']:.6f}")
|
||||
print(f"Max-tail envelope linearity R^2: {result['max_tail_envelope_r2']:.6f}")
|
||||
print(f"Figure saved to: {result['figure_path']}")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user