This commit is contained in:
Joe Ardent 2023-02-20 23:14:02 -08:00
parent 7ee27e61a5
commit 5aa7cde9aa
3 changed files with 53 additions and 26 deletions

View file

@ -68,9 +68,9 @@ pub struct CatControllerSettings {
impl Default for CatControllerSettings { impl Default for CatControllerSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
kp: 40.0, kp: 80.0,
kd: 5.0, kd: 10.0,
ki: 0.1, ki: 0.4,
} }
} }
} }

View file

@ -30,8 +30,8 @@ impl Plugin for CyberActionPlugin {
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default()) .add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
.add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_system(surface_fix.label("surface_fix")) .add_system(surface_fix.label("surface_fix"))
.add_system(gravity.before("cat")) .add_system(gravity.label("gravity").before("cat"))
.add_system(cyber_lean.before("cat")) .add_system(cyber_lean.before("cat").after("gravity"))
.add_system(falling_cat.label("cat")) .add_system(falling_cat.label("cat"))
.add_system(input_forces.label("iforces").after("cat")) .add_system(input_forces.label("iforces").after("cat"))
.add_system( .add_system(

View file

@ -1,4 +1,4 @@
use std::f32::consts::PI; use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_4};
use bevy::prelude::{ use bevy::prelude::{
Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without, Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without,
@ -19,7 +19,7 @@ use crate::{
const PITCH_SCALE: f32 = 0.2; const PITCH_SCALE: f32 = 0.2;
fn yaw_to_angle(yaw: f32) -> f32 { fn yaw_to_angle(yaw: f32) -> f32 {
yaw.powi(3) * (PI / 4.0) yaw.powi(3) * FRAC_PI_4
} }
/// The gravity vector points from the cyberbike to the center of the planet. /// The gravity vector points from the cyberbike to the center of the planet.
@ -36,21 +36,40 @@ pub(super) fn gravity(
/// The desired lean angle, given steering input and speed. /// The desired lean angle, given steering input and speed.
pub(super) fn cyber_lean( pub(super) fn cyber_lean(
velocity: Query<&Velocity, With<CyberBikeBody>>, mut bike_state: Query<
(
&Velocity,
&mut ExternalForce,
&Transform,
&ReadMassProperties,
),
With<CyberBikeBody>,
>,
wheels: Query<&Transform, With<CyberWheel>>, wheels: Query<&Transform, With<CyberWheel>>,
input: Res<InputState>, input: Res<InputState>,
gravity_settings: Res<MovementSettings>, gravity_settings: Res<MovementSettings>,
mut lean: ResMut<CyberLean>, mut lean: ResMut<CyberLean>,
) { ) {
let vel_squared = velocity.single().linvel.length_squared(); let (velocity, mut forces, xform, mass_props) = bike_state.single_mut();
let vel = velocity.linvel.dot(xform.forward());
let v_squared = vel.powi(2);
let steering_angle = yaw_to_angle(input.yaw); let steering_angle = yaw_to_angle(input.yaw);
let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect(); let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect();
let wheel_base = (wheels[0] - wheels[1]).length(); let wheel_base = (wheels[0] - wheels[1]).length();
let radius = wheel_base / steering_angle.tan(); let radius = wheel_base / steering_angle.tan();
let gravity = gravity_settings.gravity; let gravity = gravity_settings.gravity;
let tan_theta = vel_squared / (radius * gravity); let v2_r = v_squared / radius;
let tan_theta = (v2_r / gravity).clamp(-FRAC_PI_3, FRAC_PI_3);
lean.lean = (PI / 2.0) - tan_theta.atan(); let up = xform.translation.normalize();
let right = up.cross(xform.back()).normalize();
let centripetal = -right * (mass_props.0.mass * v2_r);
if v2_r.is_normal() {
forces.force += centripetal;
lean.lean = -tan_theta.atan().clamp(-FRAC_PI_4, FRAC_PI_4);
}
} }
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright. /// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
@ -62,15 +81,23 @@ pub(super) fn falling_cat(
#[cfg(feature = "inspector")] mut debug_instant: ResMut<ActionDebugInstant>, #[cfg(feature = "inspector")] mut debug_instant: ResMut<ActionDebugInstant>,
) { ) {
let (xform, mut forces, mut control_vars) = bike_query.single_mut(); let (xform, mut forces, mut control_vars) = bike_query.single_mut();
let mut wup = Transform::from_translation(xform.translation); let wup = xform.translation.normalize(); //Transform::from_translation(xform.translation.normalize());
wup.rotate(Quat::from_axis_angle(xform.back(), lean.lean)); let rot = Quat::from_axis_angle(xform.forward(), lean.lean);
let wup = wup.up(); let [x, y, z] = wup.to_array();
let qup = Quat::from_xyzw(x, y, z, 0.0); // turn our "world up" point into a Quat
// p' = rot^-1 * qup * rot
let wup = rot.inverse() * qup * rot;
let wup = Vec3::from_array([wup.x, wup.y, wup.z]).normalize();
let bike_up = xform.up(); let bike_up = xform.up();
let wright = xform.forward().cross(wup).normalize(); let wright = xform
.forward()
.cross(xform.translation.normalize())
.normalize();
let roll_error = wright.dot(bike_up); let roll_error = wright.dot(wup);
let pitch_error = wup.dot(xform.back()); let pitch_error = xform.translation.normalize().dot(xform.forward());
// only try to correct roll if we're not totally vertical // only try to correct roll if we're not totally vertical
if pitch_error.abs() < 0.95 { if pitch_error.abs() < 0.95 {
@ -80,6 +107,13 @@ pub(super) fn falling_cat(
if mag.is_finite() { if mag.is_finite() {
forces.torque += xform.back() * mag; forces.torque += xform.back() * mag;
} }
#[cfg(feature = "inspector")]
{
if debug_instant.elapsed().as_millis() > 5000 {
dbg!(&control_vars, mag, &wup);
debug_instant.reset();
}
}
} }
// we can do pitch correction any time, it's not coupled to roll // we can do pitch correction any time, it's not coupled to roll
@ -89,17 +123,10 @@ pub(super) fn falling_cat(
let mag = let mag =
(settings.kp * scaled_pitch_error) + (settings.ki * integral) + (settings.kd * derivative); (settings.kp * scaled_pitch_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_finite() { if mag.is_finite() {
forces.torque += wright * mag; //forces.torque += wright * mag;
} }
#[cfg(feature = "inspector")] //control_vars.decay();
{
if debug_instant.elapsed().as_millis() > 1000 {
dbg!(&control_vars, mag, &lean);
debug_instant.reset();
}
}
control_vars.decay();
} }
/// Apply forces to the cyberbike as a result of input. /// Apply forces to the cyberbike as a result of input.