2025-11-23 18:40:48 +00:00
|
|
|
use macroquad::prelude as mq;
|
|
|
|
|
|
|
|
|
|
mod engine;
|
|
|
|
|
use engine::*;
|
2025-11-23 23:43:48 +00:00
|
|
|
mod camera;
|
2025-12-14 22:04:04 +00:00
|
|
|
mod config;
|
2025-12-07 00:41:00 +00:00
|
|
|
mod drone;
|
2025-12-14 22:04:04 +00:00
|
|
|
mod helpers;
|
2025-11-28 20:39:09 +00:00
|
|
|
mod rendering;
|
2025-12-14 22:04:04 +00:00
|
|
|
mod simulation;
|
2025-11-23 23:43:48 +00:00
|
|
|
|
2025-11-23 18:40:48 +00:00
|
|
|
mod graphics_util;
|
|
|
|
|
|
2025-12-14 22:04:04 +00:00
|
|
|
use crate::drone::input::*;
|
|
|
|
|
use crate::simulation::{SimMode, Simulation};
|
|
|
|
|
|
|
|
|
|
use crate::config::SimulationConfig;
|
|
|
|
|
use helpers::list_files;
|
|
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
|
|
const INPUTS_DIR: &str = "inputs/";
|
|
|
|
|
const CONFIGS_DIR: &str = "configurations/";
|
|
|
|
|
const RESULTS_DIR: &str = "results/";
|
2025-11-28 20:39:09 +00:00
|
|
|
|
2025-12-08 22:34:01 +00:00
|
|
|
fn window_conf() -> mq::Conf {
|
|
|
|
|
mq::Conf {
|
|
|
|
|
window_title: "RustDroneSim".to_owned(),
|
|
|
|
|
window_resizable: true,
|
|
|
|
|
platform: mq::miniquad::conf::Platform {
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 22:04:04 +00:00
|
|
|
use std::env;
|
2025-12-15 00:08:10 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::thread::JoinHandle;
|
2025-12-14 22:04:04 +00:00
|
|
|
|
|
|
|
|
enum RunMode {
|
|
|
|
|
Batch,
|
|
|
|
|
Record { output: String },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_cli() -> RunMode {
|
|
|
|
|
let mut args = env::args().skip(1);
|
|
|
|
|
|
|
|
|
|
match args.next().as_deref() {
|
|
|
|
|
Some("record") => {
|
|
|
|
|
let output = args.next().expect("record mode requires output file path");
|
|
|
|
|
RunMode::Record { output }
|
|
|
|
|
}
|
|
|
|
|
Some("batch") | None => RunMode::Batch,
|
|
|
|
|
Some(other) => panic!("Unknown mode: {}", other),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 22:34:01 +00:00
|
|
|
#[macroquad::main(window_conf)]
|
2025-11-23 18:40:48 +00:00
|
|
|
async fn main() {
|
2025-12-14 22:04:04 +00:00
|
|
|
match parse_cli() {
|
|
|
|
|
RunMode::Batch => run_batch().await,
|
|
|
|
|
RunMode::Record { output } => run_record(output).await,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn run_batch() {
|
2025-12-15 00:08:10 +00:00
|
|
|
fs::remove_dir_all(RESULTS_DIR).unwrap();
|
2025-12-14 22:04:04 +00:00
|
|
|
fs::create_dir_all(RESULTS_DIR).unwrap();
|
|
|
|
|
|
|
|
|
|
let input_files = list_files(INPUTS_DIR);
|
|
|
|
|
let config_files = list_files(CONFIGS_DIR);
|
|
|
|
|
|
2025-12-15 00:08:10 +00:00
|
|
|
let mut handles: Vec<JoinHandle<()>> = Default::default();
|
2025-12-14 22:04:04 +00:00
|
|
|
|
2025-12-15 00:08:10 +00:00
|
|
|
for input_path in input_files {
|
2025-12-14 22:04:04 +00:00
|
|
|
for config_path in &config_files {
|
2025-12-15 00:08:10 +00:00
|
|
|
let cp = config_path.clone();
|
|
|
|
|
let ip = input_path.clone();
|
|
|
|
|
handles.push(std::thread::spawn(move || {
|
|
|
|
|
run(&ip, &cp);
|
|
|
|
|
}));
|
2025-12-14 22:04:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-15 00:08:10 +00:00
|
|
|
for handle in handles {
|
|
|
|
|
handle.join().unwrap();
|
|
|
|
|
}
|
2025-11-28 20:39:09 +00:00
|
|
|
|
2025-12-14 22:04:04 +00:00
|
|
|
println!("All simulations completed.");
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-15 00:08:10 +00:00
|
|
|
fn run(input_path: &PathBuf, config_path: &PathBuf) {
|
|
|
|
|
let input_name = input_path.file_stem().unwrap().to_string_lossy();
|
|
|
|
|
|
|
|
|
|
let inputs = InputRecording::load_inputs_from_csv(input_path.to_str().unwrap())
|
|
|
|
|
.expect("Failed to load input recording");
|
|
|
|
|
let config_name = config_path.file_stem().unwrap().to_string_lossy();
|
|
|
|
|
|
|
|
|
|
let config: SimulationConfig =
|
|
|
|
|
toml::from_str(&fs::read_to_string(config_path).unwrap()).expect("Invalid config file");
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
"Running simulation: input={} config={}",
|
|
|
|
|
input_name, config_name
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* World */
|
|
|
|
|
let mut world = World::new(config.tickrate);
|
|
|
|
|
|
|
|
|
|
/* Drone */
|
|
|
|
|
let drone = drone::Drone::new(
|
|
|
|
|
&mut world,
|
|
|
|
|
Box::new(drone::pidcontroller::PIDController {
|
|
|
|
|
target_rate: config.target_rate,
|
|
|
|
|
proportional_multiplier: config.proportional(),
|
|
|
|
|
integral_multiplier: config.integral(),
|
|
|
|
|
diferential_multiplier: config.diferential(),
|
|
|
|
|
..Default::default()
|
|
|
|
|
}),
|
|
|
|
|
drone::MotorCharacteristics {
|
|
|
|
|
max_thrust: config.max_thrust,
|
|
|
|
|
max_torque: config.max_torque,
|
|
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
config.mass,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let result_file = format!("{}/{}_{}.csv", RESULTS_DIR, input_name, config_name);
|
|
|
|
|
|
|
|
|
|
let mut sim = Simulation::new(
|
|
|
|
|
drone,
|
|
|
|
|
world,
|
|
|
|
|
false,
|
|
|
|
|
SimMode::Playback(inputs.clone(), 0.0),
|
|
|
|
|
Some(result_file),
|
|
|
|
|
config.drone_tick_rate,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
sim.run().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 22:04:04 +00:00
|
|
|
async fn run_record(output: String) {
|
|
|
|
|
println!("Recording inputs to {}", output);
|
|
|
|
|
|
|
|
|
|
let tickrate = 60000.0;
|
|
|
|
|
let mut world = World::new(tickrate);
|
|
|
|
|
|
|
|
|
|
let drone = drone::Drone::new(
|
2025-12-08 22:34:01 +00:00
|
|
|
&mut world,
|
2025-12-14 22:04:04 +00:00
|
|
|
Box::new(drone::pidcontroller::PIDController {
|
|
|
|
|
..Default::default()
|
|
|
|
|
}),
|
2025-12-08 22:34:01 +00:00
|
|
|
drone::MotorCharacteristics {
|
2025-12-10 13:11:51 +00:00
|
|
|
max_thrust: 2.6,
|
2025-12-12 11:36:13 +00:00
|
|
|
max_torque: 0.5,
|
2025-12-08 22:34:01 +00:00
|
|
|
..Default::default()
|
|
|
|
|
},
|
|
|
|
|
0.350,
|
|
|
|
|
);
|
2025-12-07 00:41:00 +00:00
|
|
|
|
2025-12-14 22:04:04 +00:00
|
|
|
let mut sim = Simulation::new(
|
|
|
|
|
drone,
|
|
|
|
|
world,
|
|
|
|
|
true,
|
|
|
|
|
SimMode::Record(InputRecording::default(), output),
|
|
|
|
|
None,
|
|
|
|
|
600,
|
2025-12-12 11:36:13 +00:00
|
|
|
);
|
|
|
|
|
|
2025-12-15 00:08:10 +00:00
|
|
|
sim.run().unwrap();
|
2025-11-23 18:40:48 +00:00
|
|
|
}
|