Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full Resolution Wheel Encoder #72

Merged
merged 28 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d11ad29
wheel_encoder_full_resolution
SM-dot May 25, 2024
e57e7f0
Update wheel_encoder.rs
SM-dot May 28, 2024
10fdbd9
Update wheel_encoder.rs
SM-dot May 28, 2024
295bb89
Update wheel_encoder.rs
SM-dot May 29, 2024
8d0a469
handling Err for wheel_encoder
SM-dot May 29, 2024
0a8eb50
Err handling in state_machine.rs
SM-dot May 29, 2024
896f575
fixing mismatched error types
SM-dot May 29, 2024
29e9198
-cleanup
SM-dot May 29, 2024
652aa82
sm - cleanup
ryescholin May 29, 2024
b8b768e
measure function only
SM-dot May 29, 2024
347d83d
simplified to a single function only
SM-dot May 29, 2024
66abaa7
fixing measure call
SM-dot May 29, 2024
14ffad8
threshold only using distance values
SM-dot May 29, 2024
4be1cf3
-cleanup
SM-dot May 29, 2024
bb98d9c
cleanup
SM-dot May 29, 2024
74c23bf
chk
ryescholin May 29, 2024
46ba52a
import cleanup
SM-dot May 29, 2024
139152f
import cleanup
SM-dot May 29, 2024
082064a
time import
SM-dot May 30, 2024
079626c
Changing pin numbers
SM-dot May 30, 2024
20f254a
Merge branch 'main' into 58-pod-improve-wheel-encoder-measurements
taesungh May 30, 2024
c4e42a6
Upgrade RPPAL to v0.18.0 for `InputPin` with RPi 5
taesungh May 30, 2024
ba569a1
Reimplement encoder transitions and measurement
taesungh May 30, 2024
2759afa
Fix wheel encoder initialization in main
taesungh May 30, 2024
ae6e33e
Fix lint issues
taesungh May 30, 2024
c562026
Add resting velocity and wheel diameter
taesungh May 30, 2024
e12acfd
Clean up encoder enums
taesungh May 30, 2024
aff1895
Remove other full-resolution copy
taesungh May 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pod-operation/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pod-operation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ enum-map = "2.7.3"
once_cell = "1.19.0"
num_enum = "0.7.2"

rppal = { version = "0.17.1", features = ["hal"] }
rppal = { version = "0.18.0", features = ["hal"] }
ina219 = "0.1.0"
ads1x1x = "0.2.2"
nb = "1.1.0"
mpu6050 = "0.1.6"
lidar_lite_v3 = "0.1.0"
i2cdev = "0.3.1"
byteorder = "1.4.3"
byteorder = "1.4.3"
184 changes: 137 additions & 47 deletions pod-operation/src/components/wheel_encoder.rs
Original file line number Diff line number Diff line change
@@ -1,76 +1,166 @@
use std::time::Instant;
use std::{ops::Sub, time::Instant};

use rppal::gpio::{Gpio, InputPin, Level};

use crate::utils::GpioPins;

const WHEEL_DIAMETER: f32 = 0.25; // feet
const ENCODER_RESOLUTION: f32 = 16.0; // pulses per revolution
const DISTANCE_PER_COUNT: f32 = WHEEL_DIAMETER * std::f32::consts::PI / ENCODER_RESOLUTION; // feet
ryescholin marked this conversation as resolved.
Show resolved Hide resolved

#[derive(Clone, Copy, num_enum::FromPrimitive, num_enum::IntoPrimitive)]
#[repr(i8)]
enum EncoderState {
A = 0b00,
B = 0b01,
C = 0b11,
D = 0b10,
#[num_enum(catch_all)]
Unknown(i8) = -1,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)]
#[repr(i8)]
enum EncoderDiff {
Backwards = -1,
Stationary = 0,
Forwards = 1,
Undersampling = 2,
#[num_enum(catch_all)]
Unknown(i8),
}

impl From<EncoderDiff> for i16 {
fn from(value: EncoderDiff) -> Self {
i16::from(i8::from(value))
}
}

impl From<EncoderDiff> for f32 {
fn from(value: EncoderDiff) -> Self {
f32::from(i8::from(value))
}
}

impl Sub for EncoderState {
type Output = EncoderDiff;

fn sub(self, other: Self) -> Self::Output {
let diff = (i8::from(self) - i8::from(other) + 5) % 4 - 1;
EncoderDiff::from(diff)
}
}

/// Encode wheel encoder state as gray code 00, 01, 11, 10
fn encode_state(a: Level, b: Level) -> EncoderState {
let state = ((a as i8) << 1) + (a as i8 ^ b as i8);
EncoderState::from(state)
}

pub struct WheelEncoder {
counter: f32,
counter: i16,
pin_a: InputPin,
pin_b: InputPin,
a_last_read: Level,
b_last_read: Level,
last_distance: f32,
last_velocity_time: Instant,
last_state: EncoderState,
last_time: Instant,
velocity: f32,
}

impl WheelEncoder {
pub fn new() -> Self {
let gpio = Gpio::new().unwrap();
let pin_a = gpio
.get(GpioPins::WHEEL_ENCODER_A.into())
.unwrap()
.into_input();
let pin_b = gpio
.get(GpioPins::WHEEL_ENCODER_B.into())
.unwrap()
.into_input();

let initial_state = encode_state(pin_a.read(), pin_b.read());

WheelEncoder {
counter: 0.0,
pin_a: gpio
.get(GpioPins::WHEEL_ENCODER_A.into())
.unwrap()
.into_input(),
pin_b: gpio
.get(GpioPins::WHEEL_ENCODER_B.into())
.unwrap()
.into_input(),
a_last_read: Level::High,
b_last_read: Level::Low,
last_distance: 0.0,
last_velocity_time: Instant::now(),
counter: 0,
last_time: Instant::now(),
last_state: initial_state,
pin_a,
pin_b,
velocity: 0.0,
}
}

pub fn read(&mut self) -> f32 {
let a_state = self.pin_a.read();
let b_state = self.pin_b.read();

if (a_state != self.a_last_read || b_state != self.b_last_read) && b_state != a_state {
self.counter += 1.0;

let current_time = Instant::now();
let distance = (self.counter / 16.0) * 3.0;
let velocity_elapsed = current_time
.duration_since(self.last_velocity_time)
.as_secs_f32();

if velocity_elapsed >= 0.1 {
let distance_delta = distance - self.last_distance;
self.velocity = distance_delta / velocity_elapsed;
self.last_velocity_time = current_time;
self.last_distance = distance;
}
pub fn measure(&mut self) -> Result<f32, &str> {
let state = self.read_state();

let inc = state - self.last_state;

if inc == EncoderDiff::Undersampling {
return Err("Wheel encoder faulted");
}

self.a_last_read = a_state;
self.b_last_read = b_state;
let time = Instant::now();
let dt = time.duration_since(self.last_time).as_secs_f32();

(self.counter / 16.0) * 3.0
}
if inc != EncoderDiff::Stationary {
self.velocity = DISTANCE_PER_COUNT * f32::from(i8::from(inc)) / dt;
self.last_time = time;
}

// When exceeding expected time to next increment, decrease velocity
if self.velocity * dt > DISTANCE_PER_COUNT {
self.velocity = DISTANCE_PER_COUNT * self.velocity.signum() / dt;
}

self.counter += i16::from(inc);
self.last_state = state;

pub fn _reset(&mut self) {
self.counter = 0.0;
self.last_distance = 0.0;
self.velocity = 0.0;
self.last_velocity_time = Instant::now();
Ok(f32::from(self.counter) * DISTANCE_PER_COUNT)
}

pub fn get_velocity(&self) -> f32 {
self.velocity
}

fn read_state(&self) -> EncoderState {
encode_state(self.pin_a.read(), self.pin_b.read())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn encoder_diff_stationary() {
let state_1 = encode_state(Level::Low, Level::High);
let state_2 = encode_state(Level::Low, Level::High);
assert_eq!(state_2 - state_1, EncoderDiff::Stationary);
}

#[test]
fn encoder_diff_forwards() {
let state_1 = encode_state(Level::High, Level::Low);
let state_2 = encode_state(Level::Low, Level::Low);
let diff = state_2 - state_1;
assert_eq!(diff, EncoderDiff::Forwards);
assert_eq!(i8::from(diff), 1);
}

#[test]
fn encoder_diff_backwards() {
let state_1 = encode_state(Level::High, Level::Low);
let state_2 = encode_state(Level::High, Level::High);
let diff = state_2 - state_1;
assert_eq!(diff, EncoderDiff::Backwards);
assert_eq!(i8::from(diff), -1);
assert_eq!(f32::from(diff), -1.0);
}

#[test]
fn encoder_diff_undersampling() {
let state_1 = encode_state(Level::High, Level::Low);
let state_2 = encode_state(Level::Low, Level::High);
assert_eq!(state_2 - state_1, EncoderDiff::Undersampling);
}
}
11 changes: 5 additions & 6 deletions pod-operation/src/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::components::lim_temperature::LimTemperature;
use crate::components::pressure_transducer::PressureTransducer;
use crate::components::signal_light::SignalLight;
use crate::components::wheel_encoder::WheelEncoder;

pub async fn blink(mut signal_light: SignalLight) {
let mut i = 0;

Expand Down Expand Up @@ -82,12 +83,10 @@ pub async fn read_gyroscope(mut gyroscope: Gyroscope) {
pub async fn read_wheel_encoder(mut wheel_encoder: WheelEncoder) {
info!("Starting wheel encoder demo.");
loop {
println!(
"{:?}{:?}",
wheel_encoder.read(),
wheel_encoder.get_velocity()
);
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
let count = wheel_encoder.measure().expect("faulted");
let velocity = wheel_encoder.get_velocity();
println!("{:?} {:?}", count, velocity);
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
}
}

Expand Down
3 changes: 2 additions & 1 deletion pod-operation/src/state_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,11 @@ impl StateMachine {
/// Perform operations when the pod is running
fn _running_periodic(&mut self) -> State {
info!("Rolling Running state");
let encoder_value = self.wheel_encoder.read(); // Read the encoder value
let encoder_value = self.wheel_encoder.measure().expect("wheel encoder faulted"); // Read the encoder value
if encoder_value > STOP_THRESHOLD {
return State::Stopped;
}

if self.downstream_pressure_transducer.read_pressure() < MIN_PRESSURE {
return State::Faulted;
}
Expand Down