-
Notifications
You must be signed in to change notification settings - Fork 0
Drive PID from odometry stamps instead of a wall-clock poll #22
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
Changes from all commits
a219aac
450508b
bc16427
b8374a2
b7c48e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ mod inotify; | |
| use std::ops::Bound; | ||
| use std::sync::Arc; | ||
| use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; | ||
| use std::time::{Duration, Instant}; | ||
| use std::time::Duration; | ||
| use arc_swap::ArcSwap; | ||
| use parry3d_f64::shape::Segment; | ||
| use tokio::sync::{Mutex, Notify}; | ||
|
|
@@ -73,6 +73,10 @@ fn wrap_angle(angle: f64) -> f64 { | |
| (angle + std::f64::consts::PI).rem_euclid(2.0 * std::f64::consts::PI) - std::f64::consts::PI | ||
| } | ||
|
|
||
| fn pose_stamp_ns(stamp: &r2r::builtin_interfaces::msg::Time) -> i64 { | ||
| i64::from(stamp.sec) * 1_000_000_000 + i64::from(stamp.nanosec) | ||
| } | ||
|
|
||
| impl MissionExecutor { | ||
| pub fn new(node: r2r::Node) -> Self { | ||
| // hardcoded so it doesn't freak out while it waits for first odometry | ||
|
|
@@ -360,12 +364,6 @@ async fn main() { | |
| } | ||
| }; | ||
|
|
||
| let consume_odometry_sub = |td: Arc<MissionExecutor>| async move { | ||
| while let Some(msg) = odometry_sub.next().await { | ||
| td.pose.store(Arc::new(Pose::from(&msg.pose.pose))); | ||
| } | ||
| }; | ||
|
|
||
| let cfg = Arc::new(ArcSwap::from_pointee(load_live_config(&live_config_path, &auv_name).unwrap())); | ||
|
|
||
| let mut inotify_stream = inotify::InotifyStream::new(); | ||
|
|
@@ -388,19 +386,35 @@ async fn main() { | |
| let go_to_goal = |td: Arc<MissionExecutor>| async move { | ||
| let mut sum_err = Vector6::zeros(); | ||
| let mut prev_pose_err = Vector6::zeros(); | ||
| let mut prev_now = Instant::now(); | ||
| let mut previous_timestamp_ns: Option<i64> = None; | ||
| let mut count = 1.0; //Technically can be an integer but since we are multiplying by float... | ||
| let log_interval = Duration::from_millis(500); | ||
| let mut last_log = Instant::now(); | ||
| while !td.stop.load(Ordering::Relaxed) { | ||
| while let Some(msg) = odometry_sub.next().await { | ||
| if td.stop.load(Ordering::Relaxed) { | ||
| break; | ||
| } | ||
|
|
||
| let pose = Pose::from(&msg.pose.pose); | ||
| td.pose.store(Arc::new(pose)); | ||
|
|
||
| let current_cfg = cfg.load(); | ||
| let PidConfig { kp, ki, kd } = current_cfg.pid[&bridge_name]; | ||
| let tam_x_y_z_roll_pitch_yaw = ¤t_cfg.tam; | ||
|
|
||
| let now = Instant::now(); | ||
| let dt = now.duration_since(prev_now).as_secs_f64(); | ||
| let timestamp_ns = pose_stamp_ns(&msg.header.stamp); | ||
| let dt = previous_timestamp_ns.and_then(|previous| { | ||
| let elapsed_ns = timestamp_ns - previous; | ||
| if elapsed_ns > 0 { | ||
| Some(elapsed_ns as f64 * 1e-9) | ||
| } else { | ||
| r2r::log_warn!( | ||
| "go_to_goal", | ||
| "odometry stamp not increasing (prev={previous} ns, now={timestamp_ns} ns); skipping I/D" | ||
| ); | ||
| None | ||
| } | ||
| }); | ||
| previous_timestamp_ns = Some(timestamp_ns); | ||
|
|
||
| let pose = **td.pose.load(); | ||
| let goal = **td.goal.load(); | ||
| let current_pose = Vector6::<f64>::from(pose); | ||
|
|
||
|
|
@@ -429,8 +443,12 @@ async fn main() { | |
|
|
||
| pose_err[5] = yaw_error; | ||
|
|
||
| let vel_err = (pose_err - prev_pose_err) / dt; | ||
| sum_err += pose_err * dt; | ||
| let vel_err = if let Some(dt) = dt { | ||
| sum_err += pose_err * dt; | ||
| (pose_err - prev_pose_err) / dt | ||
| } else { | ||
| Vector6::zeros() | ||
| }; | ||
|
|
||
| let wrench = kp.component_mul(&pose_err) | ||
| + ki.component_mul(&sum_err) | ||
|
|
@@ -473,30 +491,24 @@ async fn main() { | |
| let mut avg_curr = td.avg_current.lock().await; | ||
| *avg_curr = (*avg_curr * (count - 1.0) + sum_curr) / count; | ||
| count += 1.0; | ||
| if now.duration_since(last_log) >= log_interval { | ||
| r2r::log_info!( | ||
| "thruster_report", | ||
| "Average thruster usage in runtime: {:.2}", | ||
| *avg_curr | ||
| ); | ||
| r2r::log_info!( | ||
| "thruster_report", | ||
| "Current sum of thrusters: {:.2}", | ||
| sum_curr | ||
| ); | ||
| r2r::log_info!( | ||
| "thruster_report", | ||
| "Estimated battery life remaining: {:.2}", | ||
| BATTERY_CAPACITY / *avg_curr | ||
| ); | ||
| last_log = now; | ||
| } | ||
| r2r::log_info!( | ||
| "thruster_report", | ||
| "Average thruster usage in runtime: {:.2}", | ||
| *avg_curr | ||
| ); | ||
| r2r::log_info!( | ||
| "thruster_report", | ||
| "Current sum of thrusters: {:.2}", | ||
| sum_curr | ||
| ); | ||
| r2r::log_info!( | ||
| "thruster_report", | ||
| "Estimated battery life remaining: {:.2}", | ||
| BATTERY_CAPACITY / *avg_curr | ||
| ); | ||
| drop(avg_curr); | ||
|
|
||
| prev_pose_err = pose_err; | ||
| prev_now = now; | ||
|
|
||
| tokio::time::sleep(Duration::from_millis(100)).await; | ||
| } | ||
| }; | ||
|
|
||
|
|
@@ -533,7 +545,6 @@ async fn main() { | |
|
|
||
| tokio::spawn(consume_inotify_stream()); | ||
| tokio::spawn(consume_map_sub(Arc::clone(&td))); | ||
| tokio::spawn(consume_odometry_sub(Arc::clone(&td))); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deleted the consumer_odometry callback. Now we consume odometry inside go_to_goal.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would probably do it the other way around, so just do the "go to goal" stuff inside consume_odometry_sub but really don't matter |
||
| tokio::spawn(consume_new_objects(Arc::clone(&td))); | ||
| tokio::spawn(go_to_goal(Arc::clone(&td))); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deleted the instant::now() inside the loop. Now we get the
dtfrom the msg.header.stamp msg::TimeThis is to gaurrantee that we are using the clock of the odometry source. That its not the same in the simulation if we increase the clock rate.