From bb445989bb26c177aea0eef22ff275f03b161fad Mon Sep 17 00:00:00 2001 From: core Date: Thu, 27 Nov 2025 23:42:15 -0500 Subject: [PATCH] fix(milp): improve predictability of solver --- .../assets/config/parts/housing.part.toml | 5 --- crates/unified/assets/config/world.wc.toml | 6 +-- crates/unified/src/client/ship/thrusters.rs | 45 +++++++++++++++---- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/crates/unified/assets/config/parts/housing.part.toml b/crates/unified/assets/config/parts/housing.part.toml index c8a7b37d1035c35685619372284cf080c9d9eb29..2b2ad7c8ead3c15f0f0962d1bbabbde3fb099a58 100644 --- a/crates/unified/assets/config/parts/housing.part.toml +++ b/crates/unified/assets/config/parts/housing.part.toml @@ -13,11 +13,6 @@ id = "main" apply_force_at_local = [ 0, 0 ] thrust_vector = [ 0.0, -20000.0 ] -[[thruster]] -id = "back" -apply_force_at_local = [ 0, 0 ] -thrust_vector = [ 0.0, 20000.0 ] - [[joint]] id = "Top" target = { translation = [ 0.0, 55.0, 0.0 ], rotation = 0.0 } diff --git a/crates/unified/assets/config/world.wc.toml b/crates/unified/assets/config/world.wc.toml index cdd6334c8d21abade962d94d0efb9c6a8eb87eaf..1c4880c27052b11c6c3b8db883ee15481f38d876 100644 --- a/crates/unified/assets/config/world.wc.toml +++ b/crates/unified/assets/config/world.wc.toml @@ -7,9 +7,9 @@ spawn_parts_interval_secs = 1 default_height = 50 default_width = 50 default_mass = 100 -joint_align_compliance = 0.0002 -joint_angle_compliance = 0.00000002 -joint_limit_compliance = 0.006 +joint_align_compliance = 0.00002 +joint_angle_compliance = 0.000000002 +joint_limit_compliance = 0.0006 joint_linear_damping = 4.0 joint_angular_damping = 2.0 diff --git a/crates/unified/src/client/ship/thrusters.rs b/crates/unified/src/client/ship/thrusters.rs index 765578a71a837d79360bf73045eab3a04f24b729..ea64c9172a4613a798c860f7ce2d72ad0c87d8ed 100644 --- a/crates/unified/src/client/ship/thrusters.rs +++ b/crates/unified/src/client/ship/thrusters.rs @@ -55,6 +55,7 @@ fn draw_thruster_debug( } } +// TODO: split this into two passes fn solve_thrust( me: Query<(Option<&Parts>, &GlobalTransform, Entity), With>, parts: Query<&PartThrusters>, @@ -90,28 +91,40 @@ fn solve_thrust( // +Z == counterclockwise/ccw // -Z == clockwise/cw + const MAGIC_TORQUE_SCALE_FACTOR: f32 = 100.0; // mystery units + let mut target_unit_vector = Vec3::ZERO; + let mut anything_pressed = false; + if input.pressed(&ClientAction::ThrustForward) { + anything_pressed = true; target_unit_vector += hearty_transform.rotation() * Vec3::new(0.0, 1.0, 0.0); } if input.pressed(&ClientAction::ThrustBackward) { + anything_pressed = true; target_unit_vector += hearty_transform.rotation() * Vec3::new(0.0, -1.0, 0.0); } if input.pressed(&ClientAction::ThrustRight) { + anything_pressed = true; target_unit_vector += hearty_transform.rotation() * Vec3::new(1.0, 0.0, 0.0); } if input.pressed(&ClientAction::ThrustLeft) { + anything_pressed = true; target_unit_vector += hearty_transform.rotation() * Vec3::new(-1.0, 0.0, 0.0); } if input.pressed(&ClientAction::TorqueCw) { - target_unit_vector += Vec3::new(0.0, 0.0, -1.0); + anything_pressed = true; + target_unit_vector += Vec3::new(0.0, 0.0, -1.0 / MAGIC_TORQUE_SCALE_FACTOR); } if input.pressed(&ClientAction::TorqueCcw) { - target_unit_vector += Vec3::new(0.0, 0.0, 1.0); + anything_pressed = true; + target_unit_vector += Vec3::new(0.0, 0.0, 1.0 / MAGIC_TORQUE_SCALE_FACTOR); } - if target_unit_vector == Vec3::ZERO { + target_unit_vector = target_unit_vector.normalize(); + + if target_unit_vector == Vec3::ZERO || !anything_pressed { trace!("no buttons are pressed; zeroing thrust solution"); trace!("solved thrust in {}ms", start.elapsed().as_millis()); solution.converged = true; @@ -145,7 +158,7 @@ fn solve_thrust( // determine our rotational offset from hearty let relative_rotation = thruster_transform.rotation() * -hearty_transform.rotation(); - let thruster_torque = relative_translation.extend(0.0).cross(thruster_vector.extend(0.0)).z; + let thruster_torque = relative_translation.extend(0.0).cross(thruster_vector.extend(0.0)).z / MAGIC_TORQUE_SCALE_FACTOR; // magically assemble the worldspace vector! for the solver (not shipspace) let target_vector = thruster_vector.extend(thruster_torque); @@ -162,7 +175,22 @@ fn solve_thrust( } let coefficients = all_thrusters.iter() - .map(|u| target_unit_vector.dot(u.1)) + .map(|u| { + trace!("{} dot {}", target_unit_vector, u.1.normalize()); + target_unit_vector.dot(u.1.normalize()) + }) + .map(|u| { + trace!("=> {u}"); + // improve reliability: + // if dot is <0.1, zap it entirely (this thruster is not helping) + // TODO: figure out how to make this adjustable + if u.abs() < 0.1 { + 0.0 + } else { + u + } + }) + //.map(|u| u.powi(3)) .collect::>(); trace!("preparing model"); @@ -170,7 +198,7 @@ fn solve_thrust( // add variables to problem let variables = coefficients.iter() - .map(|u| problem.add_binary_var(*u as f64)) + .map(|u| problem.add_var(*u as f64, (0.0, 1.0))) .collect::>(); trace!("prepared {} variables; solving", variables.len()); @@ -207,8 +235,9 @@ fn solve_thrust( }; for thruster in all_thrusters.iter().enumerate() { - trace!("solution: thruster #{} ({:?}): {}", thruster.0, thruster.1.0, ssolution.var_value_rounded(variables[thruster.0])); - if ssolution.var_value_rounded(variables[thruster.0]) == 1.0 { + trace!("solution: thruster #{} ({:?}): {} @ coeff {}", thruster.0, thruster.1.0, ssolution.var_value(variables[thruster.0]), coefficients[thruster.0]); + // TODO: make this more easily adjustable + if *ssolution.var_value(variables[thruster.0]) > 0.8 { new_soln.thrusters_on.insert(*thruster.1.0); } }