sim working, need to improve graphs and data analysis
This commit is contained in:
parent
b5e582b3c7
commit
b9e5a6abd7
73
analyze.py
73
analyze.py
|
|
@ -1,57 +1,56 @@
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# Load the CSV exported by the PID controller
|
# -------- File picker --------
|
||||||
df = pd.read_csv("data.csv")
|
RESULTS_DIR = Path("results")
|
||||||
|
|
||||||
# --- Plot target vs current ---
|
csv_files = sorted(RESULTS_DIR.glob("*.csv"))
|
||||||
|
|
||||||
|
if not csv_files:
|
||||||
|
raise FileNotFoundError("No CSV files found in results/")
|
||||||
|
|
||||||
|
print("Available result files:")
|
||||||
|
for i, f in enumerate(csv_files):
|
||||||
|
print(f"[{i}] {f.name}")
|
||||||
|
|
||||||
|
choice = int(input("Select file number: "))
|
||||||
|
csv_path = csv_files[choice]
|
||||||
|
|
||||||
|
print(f"\nLoading: {csv_path}\n")
|
||||||
|
|
||||||
|
# -------- Load CSV --------
|
||||||
|
df = pd.read_csv(csv_path)
|
||||||
|
|
||||||
|
# -------- Plot target vs current --------
|
||||||
for coord in ["x", "y", "z"]:
|
for coord in ["x", "y", "z"]:
|
||||||
plt.figure()
|
plt.figure()
|
||||||
plt.plot(df["time"], df["target_" + coord], label="target_" + coord)
|
plt.plot(df["time"], df[f"target_{coord}"], label=f"target_{coord}")
|
||||||
plt.plot(df["time"], df["current_" + coord], label="current_" + coord)
|
plt.plot(df["time"], df[f"current_{coord}"], label=f"current_{coord}")
|
||||||
plt.plot(df["time"], df["mot_" + coord].clip(-1,1), label="mot_" + coord)
|
plt.plot(df["time"], df[f"mot_{coord}"].clip(-1, 1), label=f"mot_{coord}")
|
||||||
plt.plot(df["time"], df["dmot_" + coord].clip(-1,1), label="desired mot_" + coord)
|
plt.plot(df["time"], df[f"dmot_{coord}"].clip(-1, 1), label=f"desired mot_{coord}")
|
||||||
|
|
||||||
plt.xlabel("Time (s)")
|
plt.xlabel("Time (s)")
|
||||||
plt.ylabel("Angular velocity & Motor Offset")
|
plt.ylabel("Angular velocity & Motor Offset")
|
||||||
plt.title(f"Target vs Current Angular Velocity with Motor Diff ({coord})")
|
plt.title(f"{csv_path.name} — {coord.upper()} axis")
|
||||||
plt.legend(loc='upper right')
|
plt.legend(loc="upper right")
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
|
|
||||||
|
ymin, ymax = plt.ylim()
|
||||||
|
plt.ylim(min(ymin, -1), max(ymax, 1))
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
# for coord in ["x", "y", "z"]:
|
# -------- Plot error --------
|
||||||
# plt.figure()
|
|
||||||
# plt.plot(df["time"], df["target_" + coord], label="target_" + coord)
|
|
||||||
# plt.plot(df["time"], df["current_" + coord], label="current_" + coord)
|
|
||||||
# plt.xlabel("Time (s)")
|
|
||||||
# plt.ylabel("Angular velocity")
|
|
||||||
# plt.title(f"Target vs Current Angular Velocity ({coord})")
|
|
||||||
# plt.legend()
|
|
||||||
# plt.grid(True)
|
|
||||||
# plt.show()
|
|
||||||
|
|
||||||
|
|
||||||
# plt.figure()
|
|
||||||
# plt.plot(df["time"], df["target_x"], label="target_x")
|
|
||||||
# plt.plot(df["time"], df["current_x"], label="current_x")
|
|
||||||
# plt.plot(df["time"], df["target_y"], label="target_y")
|
|
||||||
# plt.plot(df["time"], df["current_y"], label="current_y")
|
|
||||||
# plt.plot(df["time"], df["target_z"], label="target_z")
|
|
||||||
# plt.plot(df["time"], df["current_z"], label="current_z")
|
|
||||||
# plt.xlabel("Time (s)")
|
|
||||||
# plt.ylabel("Angular velocity")
|
|
||||||
# plt.title("Target vs Current Angular Velocity")
|
|
||||||
# plt.legend()
|
|
||||||
# plt.grid(True)
|
|
||||||
# plt.show()
|
|
||||||
# --- Plot error over time ---
|
|
||||||
plt.figure()
|
plt.figure()
|
||||||
plt.plot(df["time"], df["error_x"], label="error_x")
|
plt.plot(df["time"], df["error_x"], label="error_x")
|
||||||
plt.plot(df["time"], df["error_y"], label="error_y")
|
plt.plot(df["time"], df["error_y"], label="error_y")
|
||||||
plt.plot(df["time"], df["error_z"], label="error_z")
|
plt.plot(df["time"], df["error_z"], label="error_z")
|
||||||
|
|
||||||
plt.xlabel("Time (s)")
|
plt.xlabel("Time (s)")
|
||||||
plt.ylabel("Error")
|
plt.ylabel("Error")
|
||||||
plt.title("PID Error Over Time")
|
plt.title(f"PID Error — {csv_path.name}")
|
||||||
plt.legend(loc='upper right')
|
plt.legend(loc="upper right")
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
|
||||||
34
src/main.rs
34
src/main.rs
|
|
@ -34,6 +34,8 @@ fn window_conf() -> mq::Conf {
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::thread::JoinHandle;
|
||||||
|
|
||||||
enum RunMode {
|
enum RunMode {
|
||||||
Batch,
|
Batch,
|
||||||
|
|
@ -62,23 +64,39 @@ async fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_batch() {
|
async fn run_batch() {
|
||||||
|
fs::remove_dir_all(RESULTS_DIR).unwrap();
|
||||||
fs::create_dir_all(RESULTS_DIR).unwrap();
|
fs::create_dir_all(RESULTS_DIR).unwrap();
|
||||||
|
|
||||||
let input_files = list_files(INPUTS_DIR);
|
let input_files = list_files(INPUTS_DIR);
|
||||||
let config_files = list_files(CONFIGS_DIR);
|
let config_files = list_files(CONFIGS_DIR);
|
||||||
|
|
||||||
|
let mut handles: Vec<JoinHandle<()>> = Default::default();
|
||||||
|
|
||||||
for input_path in input_files {
|
for input_path in input_files {
|
||||||
|
for config_path in &config_files {
|
||||||
|
let cp = config_path.clone();
|
||||||
|
let ip = input_path.clone();
|
||||||
|
handles.push(std::thread::spawn(move || {
|
||||||
|
run(&ip, &cp);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("All simulations completed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(input_path: &PathBuf, config_path: &PathBuf) {
|
||||||
let input_name = input_path.file_stem().unwrap().to_string_lossy();
|
let input_name = input_path.file_stem().unwrap().to_string_lossy();
|
||||||
|
|
||||||
let inputs = InputRecording::load_inputs_from_csv(input_path.to_str().unwrap())
|
let inputs = InputRecording::load_inputs_from_csv(input_path.to_str().unwrap())
|
||||||
.expect("Failed to load input recording");
|
.expect("Failed to load input recording");
|
||||||
|
|
||||||
for config_path in &config_files {
|
|
||||||
let config_name = config_path.file_stem().unwrap().to_string_lossy();
|
let config_name = config_path.file_stem().unwrap().to_string_lossy();
|
||||||
|
|
||||||
let config: SimulationConfig =
|
let config: SimulationConfig =
|
||||||
toml::from_str(&fs::read_to_string(config_path).unwrap())
|
toml::from_str(&fs::read_to_string(config_path).unwrap()).expect("Invalid config file");
|
||||||
.expect("Invalid config file");
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Running simulation: input={} config={}",
|
"Running simulation: input={} config={}",
|
||||||
|
|
@ -117,11 +135,7 @@ async fn run_batch() {
|
||||||
config.drone_tick_rate,
|
config.drone_tick_rate,
|
||||||
);
|
);
|
||||||
|
|
||||||
sim.run().await.unwrap();
|
sim.run().unwrap();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("All simulations completed.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_record(output: String) {
|
async fn run_record(output: String) {
|
||||||
|
|
@ -152,5 +166,5 @@ async fn run_record(output: String) {
|
||||||
600,
|
600,
|
||||||
);
|
);
|
||||||
|
|
||||||
sim.run().await.unwrap();
|
sim.run().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ impl Simulation {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> Result<(), Box<dyn Error>> {
|
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
let mut current_input: JoystickInput;
|
let mut current_input: JoystickInput;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -97,16 +97,16 @@ impl Simulation {
|
||||||
renderer.update_camera(&self.world);
|
renderer.update_camera(&self.world);
|
||||||
}
|
}
|
||||||
|
|
||||||
if mq::is_key_pressed(mq::KeyCode::Q) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Input handling ---
|
// --- Input handling ---
|
||||||
let current_time = self.world.get_time() as f32;
|
let current_time = self.world.get_time() as f32;
|
||||||
|
|
||||||
match &mut self.mode {
|
match &mut self.mode {
|
||||||
SimMode::Record(recording, _) => {
|
SimMode::Record(recording, _) => {
|
||||||
current_input = recording.add_input_from_keyboard(current_time);
|
current_input = recording.add_input_from_keyboard(current_time);
|
||||||
|
if mq::is_key_pressed(mq::KeyCode::Q) {
|
||||||
|
self.shutdown()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SimMode::Playback(recording, start_time) => {
|
SimMode::Playback(recording, start_time) => {
|
||||||
let playback_time = current_time - *start_time;
|
let playback_time = current_time - *start_time;
|
||||||
|
|
@ -187,7 +187,7 @@ impl Simulation {
|
||||||
== 0
|
== 0
|
||||||
{
|
{
|
||||||
renderer.draw(&mut self.world);
|
renderer.draw(&mut self.world);
|
||||||
mq::next_frame().await;
|
mq::next_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue