Skip to content

Braking system improvements#17

Open
LykAunn wants to merge 1 commit into
s2527254/hyp-11-create-motor-control-boardfrom
braking-loop-improvements
Open

Braking system improvements#17
LykAunn wants to merge 1 commit into
s2527254/hyp-11-create-motor-control-boardfrom
braking-loop-improvements

Conversation

@LykAunn
Copy link
Copy Markdown

@LykAunn LykAunn commented Feb 1, 2026

Upgraded the braking loop from a passive monitor to an active fail-safe system. This PR introduces communication timeouts and accounts for mechanical latency in stopping distance calculations.

Key changes:

  • Heartbeat Fail-safe: The system would now automatically trigger the emergency brakes if navigation data is lost. The timeout value DATA_TIMEOUT can be modified but is set to a default value of 100ms.
  • Latency: Updated the stopping formula to include reaction time distance when determining whether to engage the brakes while the pod is moving.
  • Moving data-loss protection: If the position of the pod is lost while the velocity is > 1.0 m/s, the system would perform an emergency stop.

The previous loop could hang indefinitely if it doesnt receive data from navigation. These changes ensure that the pod defaults to a safe state under any data loss.

@LykAunn LykAunn requested a review from davidbeechey as a code owner February 1, 2026 23:06
@LykAunn LykAunn self-assigned this Feb 1, 2026
loop {
let update = nav_rx.receive().await;
latest = latest.merge(update);
brake_solenoid_pin.set_high(); // Unclamp brakes (Ensure brakes start at known state)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could go above and beyond and macro this so that it's even clearer to read, something like:

macro_rules! unclamp_brakes {
    () => {
        brake_solenoid_pin.set_high();
    };
}

macro_rules! clamp_brakes {
    () => {
        brake_solenoid_pin.set_low();
    };
}

then you can use unclamp_brakes!() and clamp_brakes!() in your code instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, you seem to have engage_brake_solenoid and release_brake_solenoid defined in this crate and imported in this file which you could use too?

continue;
}
// Distance travelled during reaction time (d = v * t)
let reaction_dist = velocity * reaction_time_s;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is reaction_time_s defined? It should probably be read from the config file, or if it's a constant in the code it should be REACTION_TIME_S.

continue;
}
// Distance travelled during reaction time (d = v * t)
let reaction_dist = velocity * reaction_time_s;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let reaction_dist = velocity * reaction_time_s;
let reaction_distance = velocity * reaction_time_s;

let mut brakes_engaged = false;

// Safety Parameters
let max_brake_decel = MAX_BRAKE_FORCE_N / POD_MASS_KG; // m/s^2
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let max_brake_decel = MAX_BRAKE_FORCE_N / POD_MASS_KG; // m/s^2
let max_brake_deceleration = MAX_BRAKE_FORCE_N / POD_MASS_KG; // m/s^2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, MAX_BRAKE_FORCE_N and POD_MASS_KG also sound like they should be in the config file and not constants.

if velocity > 0.1 && distance_to_target <= stopping_distance {
// Total required buffer distance to fully brake
let required_stop_buffer_distance = reaction_dist + physical_braking_distance + SAFETY_MARGIN_M;
// SAFETY_MARGIN_M from line 57 (to be implemented)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still to be implemented? Also sounds like something that could go in the config file

Comment on lines +79 to 98
if velocity > 0.1 && projected_stop_point >= target {
if !brakes_engaged {
defmt::info!(
"Engaging brakes: pos={} m, target={} m, dist={} m, vel={} m/s, stop_dist={} m",
defmt::warn!(
"BRAKING POINT REACHED: Pos={:.2} m, Vel={:.2}, Target={:.2}, Proj_stop={:.2}",
position,
target,
distance_to_target,
velocity,
stopping_distance
target,
projected_stop_point
);
// Tell motor controller to stop drive before clamping.
enqueue_canopen(Messages::QuickStop).await;
enqueue_canopen(Messages::Shutdown).await;
// Brake solenoid clamps when driven low (per pneumatics::engage_brakes comments).
engage_brake_solenoid().await;
brake_solenoid_pin.set_low();
brakes_engaged = true;
}
} else {
// Keep brakes released until within stopping distance.
if brakes_engaged {
defmt::info!("Brakes remain engaged (no auto-release implemented)");
} else {
release_brake_solenoid().await;
brake_solenoid_pin.set_high();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the logic could be tidied up a bit, e.g. checking if the brakes are engaged and using continue instead of nesting ifs

defmt::info!("Braking system active (mechanical brakes via GPIO)");

let mut latest = NavKinematics::default();
let mut brakes_engaged = false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is required if you're expecting it to be the same as the value on the pin, because you can access that with brake_solenoid_pin.is_set_high() or brake_solenoid_pin.get_output_level() (docs)

Comment on lines +52 to +60
(_, _, Some(v)) if v > 1.0 => {
if !brakes_engaged {
defmt::error!("CRITICAL: Lost Position/Target while moving (v={})! Engaging emergency brakes.", v)
enqueue_canopen(Messages::QuickStop).await;
brake_solenoid_pin.set_low();
brakes_engaged = true;
}
continue;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the intention behind handling this separately? Would be good to pop a comment above it with the rationale

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants