refactor: 重构架构并整理仓库

This commit is contained in:
Horldsence
2026-09-23 10:56:57 +08:00
parent 680adae339
commit f8aeafdfc2
101 changed files with 14417 additions and 11399 deletions
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "imu"
description = "IMU data contract"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[lints]
workspace = true
[dependencies]
+131
View File
@@ -0,0 +1,131 @@
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
//! IMU 抽象:姿态数据与读取接口(ARCHITECTURE.md §5.1)。
//!
//! `driver crates::delta10` 为 DETA10 实现 [`Imu`];测试可用
//! `motor::testing` 风格的替身。
//!
//! # 单位与坐标系
//!
//! 与 ARCHITECTURE.md §5.1 的 [`ImuData`] 一致:
//!
//! | 字段 | 单位 | 坐标系 |
//! | --- | --- | --- |
//! | `accel` | m/s² | 机体系(x 前、y 左、z 上) |
//! | `gyro` | rad/s | 机体系 |
//! | `mag` | µT | 机体系 |
//! | `roll` / `pitch` / `yaw` | 度 | 机体 → 世界(yaw 逆时针为正) |
//!
//! 时间戳为单调时钟纳秒(见 [`crate::time`]),用于和电机反馈对齐。
/// 一帧 IMU 数据。
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct ImuData {
/// 加速度(m/s²,机体系 x/y/z)。
pub accel: [f32; 3],
/// 角速度(rad/s,机体系 x/y/z)。
pub gyro: [f32; 3],
/// 磁力计(µT,机体系 x/y/z);无磁力计时为 `[0.0; 3]`。
pub mag: [f32; 3],
/// 横滚角(度)。
pub roll: f32,
/// 俯仰角(度)。
pub pitch: f32,
/// 偏航角(度)。
pub yaw: f32,
/// 单调时钟时间戳(纳秒)。
pub timestamp_ns: u64,
}
impl ImuData {
/// 所有字段是否有限(NaN / inf 不得进入控制律)。
#[must_use]
pub fn is_finite(&self) -> bool {
self.accel.iter().chain(&self.gyro).chain(&self.mag).all(|v| v.is_finite())
&& self.roll.is_finite()
&& self.pitch.is_finite()
&& self.yaw.is_finite()
}
}
/// IMU 读取错误。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImuError {
/// 链路关闭。
Closed,
/// 帧 ID / 长度不符(多半是总线接错或协议模式不对)。
BadFrame,
/// **帧载荷布局尚未确认**(ARCHITECTURE.md 决策点 D1)。
///
/// 见 `delta10-protocol`:DETA10 手册链接已失效,载荷偏移在
/// 拿到数据手册前不得臆造,因此解码器会显式返回本错误。
LayoutUnconfirmed,
/// 校验和 / 校验失败。
Checksum,
/// 底层 IO 失败。
Io,
}
impl core::fmt::Display for ImuError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Closed => write!(f, "IMU 链路已关闭"),
Self::BadFrame => write!(f, "IMU 帧 ID 或长度不符"),
Self::LayoutUnconfirmed => {
write!(f, "IMU 载荷布局未确认(ARCHITECTURE.md 决策点 D1:需 DETA10 数据手册)")
}
Self::Checksum => write!(f, "IMU 校验失败"),
Self::Io => write!(f, "IMU 底层 IO 失败"),
}
}
}
impl core::error::Error for ImuError {}
/// 硬件无关的 IMU 边界。
pub trait Imu {
/// 底层错误类型。
type Error: core::error::Error + Send + Sync + 'static;
/// 读取一帧姿态数据(阻塞直到有帧或链路关闭)。
///
/// # Errors
///
/// 链路关闭、帧不合法、载荷布局未确认时返回错误。
fn read(&mut self) -> impl core::future::Future<Output = Result<ImuData, Self::Error>>;
/// 非阻塞读取:本轮没有新帧时返回 `Ok(None)`。
///
/// # Errors
///
/// 仅在链路确定损坏时返回错误。
fn try_read(&mut self) -> Result<Option<ImuData>, Self::Error> {
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_data_is_zero_and_finite() {
let data = ImuData::default();
assert!((data.yaw).abs() < 1e-6);
assert!(data.is_finite());
}
#[test]
fn non_finite_attitude_is_detected() {
let data = ImuData { pitch: f32::NAN, ..ImuData::default() };
assert!(!data.is_finite());
let data = ImuData { gyro: [0.0, f32::INFINITY, 0.0], ..ImuData::default() };
assert!(!data.is_finite());
}
#[test]
fn layout_unconfirmed_error_names_the_decision_point() {
let text = ImuError::LayoutUnconfirmed.to_string();
assert!(text.contains("D1"), "{text}");
}
}