From f7cced1c6960ad768703576c72008d86606bedbc Mon Sep 17 00:00:00 2001 From: Maximilian Schmidt Date: Fri, 3 Jul 2026 11:34:51 +0200 Subject: [PATCH 1/4] feat: recover low state source timestamps --- Cargo.lock | 2 + crates/nodes/low_state_bridge/Cargo.toml | 2 + crates/nodes/low_state_bridge/src/lib.rs | 454 ++++++++++++++++++++- crates/ros-z/src/pubsub/publisher.rs | 58 ++- crates/ros-z/tests/pubsub.rs | 48 +++ crates/ros2/src/sensor_msgs/joint_state.rs | 13 + crates/ros2/src/sensor_msgs/mod.rs | 1 + 7 files changed, 556 insertions(+), 22 deletions(-) create mode 100644 crates/ros2/src/sensor_msgs/joint_state.rs diff --git a/Cargo.lock b/Cargo.lock index e3499029a7..819fa8fdcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6596,7 +6596,9 @@ dependencies = [ "color-eyre", "kinematics", "ros-z", + "ros2", "tokio", + "tracing", ] [[package]] diff --git a/crates/nodes/low_state_bridge/Cargo.toml b/crates/nodes/low_state_bridge/Cargo.toml index f1103ac350..609672ebbe 100644 --- a/crates/nodes/low_state_bridge/Cargo.toml +++ b/crates/nodes/low_state_bridge/Cargo.toml @@ -11,4 +11,6 @@ cdr = { workspace = true } color-eyre = { workspace = true } kinematics = { workspace = true } ros-z = { workspace = true } +ros2 = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal"] } +tracing = { workspace = true } diff --git a/crates/nodes/low_state_bridge/src/lib.rs b/crates/nodes/low_state_bridge/src/lib.rs index fdce0aa655..28296affd8 100644 --- a/crates/nodes/low_state_bridge/src/lib.rs +++ b/crates/nodes/low_state_bridge/src/lib.rs @@ -1,3 +1,4 @@ +use std::collections::VecDeque; use std::sync::Arc; use std::{boxed::Box, future::Future, pin::Pin}; @@ -5,12 +6,244 @@ use color_eyre::{Result, eyre::Context as _}; use booster::{ImuState, LowState, MotorState}; use kinematics::joints::Joints; -use ros_z::prelude::*; +use ros_z::{prelude::*, time::Time}; +use ros2::sensor_msgs::joint_state::JointState; pub fn run_boxed(ctx: Arc) -> Pin> + Send>> { Box::pin(run(ctx)) } +const SAMPLE_BUFFER_LIMIT: usize = 128; + +#[derive(Clone, Debug, PartialEq, Eq)] +struct SampleKey { + positions: Vec, + velocities: Vec, + efforts: Vec, +} + +impl SampleKey { + fn from_low_state(low_state: &LowState) -> Self { + Self::from_f32_parts( + low_state + .motor_state_serial + .iter() + .map(|motor_state| motor_state.position), + low_state + .motor_state_serial + .iter() + .map(|motor_state| motor_state.velocity), + low_state + .motor_state_serial + .iter() + .map(|motor_state| motor_state.torque), + ) + } + + fn from_joint_state(joint_state: &JointState) -> Self { + Self::from_f32_parts( + joint_state.position.iter().map(|position| *position as f32), + joint_state.velocity.iter().map(|velocity| *velocity as f32), + joint_state.effort.iter().map(|effort| *effort as f32), + ) + } + + fn from_f32_parts( + positions: impl IntoIterator, + velocities: impl IntoIterator, + efforts: impl IntoIterator, + ) -> Self { + Self { + positions: positions.into_iter().map(f32::to_bits).collect(), + velocities: velocities.into_iter().map(f32::to_bits).collect(), + efforts: efforts.into_iter().map(f32::to_bits).collect(), + } + } +} + +#[derive(Debug)] +struct PendingLowState { + sequence_number: u64, + key: SampleKey, + low_state: LowState, +} + +#[derive(Debug)] +struct JointStateStamp { + key: SampleKey, + source_time: Time, +} + +#[derive(Debug)] +struct MatchedLowState { + low_state: LowState, + source_time: Time, +} + +#[derive(Debug, Default)] +struct DropCounters { + low_state_buffer_overflows: u64, + joint_state_buffer_overflows: u64, + non_monotonic_low_states: u64, +} + +#[derive(Debug, Default)] +struct LowStateMatcher { + pending_low_states: VecDeque, + joint_state_stamps: VecDeque, + next_sequence_number: u64, + last_published_source_time: Option