#[derive(Debug, Clone)]
pub struct PidState {
pub kp: f64,
pub ki: f64,
pub kd: f64,
pub integral: f64,
pub prev_error: f64,
pub integral_max: f64,
}
impl PidState {
pub fn new(kp: f64, ki: f64, kd: f64) -> Self {
Self {
kp,
ki,
kd,
integral: 0.0,
prev_error: 0.0,
integral_max: 100.0,
}
}
pub fn compute(&mut self, setpoint: f64, actual: f64, dt_s: f64) -> f64 {
let error = setpoint - actual;
let dt = if dt_s <= 0.0 { 0.005 } else { dt_s };
let p_term = self.kp * error;
self.integral += error * dt;
self.integral = self.integral.clamp(-self.integral_max, self.integral_max);
let i_term = self.ki * self.integral;
let d_error = (error - self.prev_error) / dt;
self.prev_error = error;
let d_term = self.kd * d_error;
p_term + i_term + d_term
}
}
use std::cell::RefCell;
thread_local! {
static DEFAULT_PID: RefCell<PidState> = RefCell::new(PidState::new(1.0, 0.0, 0.0));
}
pub fn pid_compute(setpoint: f64, actual: f64, kp: f64, ki: f64, kd: f64) -> f64 {
DEFAULT_PID.with(|pid_cell| {
let mut pid = pid_cell.borrow_mut();
pid.kp = kp;
pid.ki = ki;
pid.kd = kd;
pid.compute(setpoint, actual, 0.005).clamp(-100.0, 100.0)
})
}
pub fn pure_pursuit(
rx: f64,
ry: f64,
rtheta: f64,
wx: f64,
wy: f64,
linear_v: f64,
) -> (f64, f64) {
let dx = wx - rx;
let dy = wy - ry;
let local_x = dx * rtheta.cos() + dy * rtheta.sin();
let local_y = -dx * rtheta.sin() + dy * rtheta.cos();
let l2 = (local_x * local_x + local_y * local_y).max(0.01);
let curvature = 2.0 * local_y / l2;
let angular_w = linear_v * curvature;
(curvature, angular_w)
}
pub fn feedforward_pid(
setpoint: f64,
actual: f64,
kp: f64,
ki: f64,
kd: f64,
kf: f64,
prev_setpoint: f64,
dt_s: f64,
) -> f64 {
let dt = if dt_s <= 0.0 { 0.005 } else { dt_s };
let ff_term = kf * ((setpoint - prev_setpoint) / dt);
let pid_term = pid_compute(setpoint, actual, kp, ki, kd);
(ff_term + pid_term).clamp(-100.0, 100.0)
}
pub fn ekf_update(
prior_x: f64,
meas_z: f64,
cov_p: f64,
meas_noise_r: f64,
) -> (f64, f64) {
let r = if meas_noise_r <= 0.0 { 0.1 } else { meas_noise_r };
let kalman_gain = cov_p / (cov_p + r);
let updated_x = prior_x + kalman_gain * (meas_z - prior_x);
let updated_cov = (1.0 - kalman_gain) * cov_p;
(updated_x, updated_cov)
}
pub fn fuse_imu_gps(lat: f64, lon: f64, yaw_deg: f64) -> (f64, f64, f64) {
let mut yaw_norm = yaw_deg % 360.0;
if yaw_norm > 180.0 {
yaw_norm -= 360.0;
} else if yaw_norm < -180.0 {
yaw_norm += 360.0;
}
let corrected_lat = lat + (yaw_norm.to_radians().cos() * 0.00001);
let corrected_lon = lon + (yaw_norm.to_radians().sin() * 0.00001);
(corrected_lat, corrected_lon, yaw_norm)
}
pub fn ik_3dof_arm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
let l1 = 100.0_f64;
let l2 = 120.0_f64;
let l3 = 100.0_f64;
let base_rad = y.atan2(x);
let base_deg = base_rad.to_degrees().clamp(0.0, 180.0);
let r = (x * x + y * y).sqrt();
let r_eff = (r - 20.0).max(1.0); let z_eff = z - l1;
let d2 = r_eff * r_eff + z_eff * z_eff;
let d = d2.sqrt().clamp(10.0, l2 + l3 - 0.1);
let cos_elbow = ((d2 - l2 * l2 - l3 * l3) / (2.0 * l2 * l3)).clamp(-1.0, 1.0);
let elbow_rad = cos_elbow.acos();
let elbow_deg = elbow_rad.to_degrees().clamp(0.0, 180.0);
let alpha = z_eff.atan2(r_eff);
let cos_beta = ((l2 * l2 + d2 - l3 * l3) / (2.0 * l2 * d)).clamp(-1.0, 1.0);
let beta = cos_beta.acos();
let shoulder_rad = alpha + beta;
let shoulder_deg = shoulder_rad.to_degrees().clamp(0.0, 180.0);
(base_deg, shoulder_deg, elbow_deg)
}
pub fn diff_drive_ik(linear_v: f64, angular_w: f64) -> (i32, i32) {
let wheel_base = 0.30_f64; let max_velocity = 1.5_f64;
let v_left = linear_v - (angular_w * wheel_base / 2.0);
let v_right = linear_v + (angular_w * wheel_base / 2.0);
let pwm_left = ((v_left / max_velocity) * 100.0).round().clamp(-100.0, 100.0) as i32;
let pwm_right = ((v_right / max_velocity) * 100.0).round().clamp(-100.0, 100.0) as i32;
(pwm_left, pwm_right)
}
pub fn lqr_compute(err_x: f64, err_v: f64, k1: f64, k2: f64) -> f64 {
let control_input = -(k1 * err_x + k2 * err_v);
control_input.clamp(-100.0, 100.0)
}
pub fn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
let r_earth = 6_371_000.0_f64; let d_lat = (lat2 - lat1).to_radians();
let d_lon = (lon2 - lon1).to_radians();
let a = (d_lat / 2.0).sin().powi(2)
+ lat1.to_radians().cos() * lat2.to_radians().cos() * (d_lon / 2.0).sin().powi(2);
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
r_earth * c
}
pub fn lidar_find_closest(scan: &[f64]) -> (f64, u32) {
let mut min_dist = f64::MAX;
let mut min_angle = 0;
for (angle, &dist) in scan.iter().enumerate() {
if dist > 0.1 && dist < min_dist {
min_dist = dist;
min_angle = angle as u32;
}
}
if min_dist == f64::MAX {
(999.0, 0)
} else {
(min_dist, min_angle)
}
}
pub struct StdlibModule {
pub name: &'static str,
pub description: &'static str,
pub functions: &'static [(&'static str, &'static str)],
}
pub const STDLIB_MODULES: &[StdlibModule] = &[
StdlibModule {
name: "std::gpio",
description: "General Purpose Input/Output Pin Control, Modes, Interrupts",
functions: &[
("write(pin: u32, level: bool) -> void", "Write HIGH or LOW to a digital output pin"),
("read(pin: u32) -> bool", "Read digital state from an input pin"),
("set_mode(pin: u32, mode: PinMode) -> void", "Set pin mode to IN, OUT, ALT, or PWM"),
("attach_interrupt(pin: u32, edge: String, callback: fn) -> void", "Attach a hardware interrupt handler"),
],
},
StdlibModule {
name: "std::i2c",
description: "I2C Master/Slave Communication & Sensor Interface",
functions: &[
("read(bus: u32, addr: u8, len: usize) -> [u8; 32]", "Read bytes from an I2C slave address"),
("write(bus: u32, addr: u8, data: [u8; 32]) -> void", "Write bytes to an I2C slave address"),
("scan(bus: u32) -> [u8; 16]", "Scan bus for active I2C slave addresses"),
],
},
StdlibModule {
name: "std::spi",
description: "SPI High-Speed Peripheral Bus & Display/SD-Card Driver",
functions: &[
("transfer(bus: u32, tx: u8) -> u8", "Simultaneous full-duplex SPI byte transfer"),
("write_buffer(bus: u32, data: [u8; 64]) -> void", "Burst write buffer over SPI"),
],
},
StdlibModule {
name: "std::uart",
description: "Serial UART Telemetry, GPS, and LIDAR Communication",
functions: &[
("send(port: u32, data: string) -> void", "Transmit string telemetry over serial port"),
("receive(port: u32) -> string", "Receive string packet from UART rx buffer"),
("set_baudrate(port: u32, baud: u32) -> void", "Configure serial port baudrate (e.g. 115200)"),
],
},
StdlibModule {
name: "std::can",
description: "CAN-Bus automotive and multi-node industrial robotics protocol",
functions: &[
("send(id: u32, data: [u8; 8], extended: bool) -> void", "Send a standard or extended CAN frame"),
("receive() -> (u32, [u8; 8])", "Read next incoming CAN frame from bus queue"),
("set_filter(id: u32, mask: u32) -> void", "Configure hardware CAN acceptance filters"),
],
},
StdlibModule {
name: "std::pwm",
description: "Pulse Width Modulation for Servo Motors and LED Dimming",
functions: &[
("write(pin: u32, duty_cycle: u32) -> void", "Set PWM duty cycle between 0% and 100%"),
("set_frequency(pin: u32, freq_hz: u32) -> void", "Set PWM frequency in Hertz"),
],
},
StdlibModule {
name: "std::bluetooth",
description: "Bluetooth Low Energy (BLE) GATT Server & Remote Controller",
functions: &[
("advertise(name: string) -> void", "Start BLE advertising with custom device name"),
("notify(char_uuid: string, value: string) -> void", "Send BLE GATT notification to connected client"),
],
},
StdlibModule {
name: "std::wifi",
description: "Wi-Fi AP/Station Mode & TCP/UDP Telemetry Sockets",
functions: &[
("connect(ssid: string, psk: string) -> bool", "Connect to wireless access point"),
("start_ap(ssid: string) -> void", "Start Access Point mode for local robot config"),
],
},
StdlibModule {
name: "std::usb",
description: "USB CDC/ACM Serial, HID, and USB-OTG Device Mode",
functions: &[
("send_cdc(data: string) -> void", "Send data over USB CDC Virtual Com Port"),
("read_cdc() -> string", "Read data from USB CDC endpoint"),
],
},
StdlibModule {
name: "std::ros",
description: "Native ROS 2 Pub/Sub Messaging Layer & Topic Bridge",
functions: &[
("init(node_name: string) -> void", "Initialize ROS 2 node context"),
("publish(topic: string, msg: string) -> void", "Publish payload to ROS 2 topic"),
("subscribe(topic: string, callback: fn) -> void", "Subscribe to ROS 2 topic with callback"),
],
},
StdlibModule {
name: "std::motor",
description: "DC, Stepper, ESC/BLDC Drivers, Differential Drive & PID Controller",
functions: &[
("set_speed(channel: u32, speed: i32) -> void", "Set motor speed (-100 to 100)"),
("pid_compute(setpoint: f64, actual: f64, kp: f64, ki: f64, kd: f64) -> f64", "Compute PID control output"),
("stop_all() -> void", "Emergency stop all motor channels"),
],
},
StdlibModule {
name: "std::sensor",
description: "High-level Sensor Abstractions (Ultrasonic, IMU, Encoders, LIDAR)",
functions: &[
("ultrasonic_read(trig: u32, echo: u32) -> f64", "Read ultrasonic distance in centimeters"),
("imu_read_rpy() -> (f64, f64, f64)", "Read roll, pitch, yaw from IMU sensor fusion"),
("encoder_count(channel: u32) -> i64", "Get optical encoder pulse count"),
],
},
StdlibModule {
name: "std::camera",
description: "Vision Pipeline, Frame Buffer Acquisition, Edge Detection & Blob Tracking",
functions: &[
("capture() -> [u8; 1024]", "Capture frame from camera sensor"),
("detect_color_blob(r: u8, g: u8, b: u8) -> (f64, f64)", "Detect center (X,Y) of color blob in image"),
],
},
StdlibModule {
name: "std::ai",
description: "Lightweight Robotics Edge AI & Neural Network Inference Engine",
functions: &[
("infer(model_id: string, inputs: [f64; 16]) -> [f64; 4]", "Run edge AI model inference on sensor tensor"),
("classify_obstacle(distance_cm: f64, camera_score: f64) -> string", "Classify obstacle type (Wall, Person, Unknown)"),
],
},
StdlibModule {
name: "std::thread",
description: "Concurrent Execution, Mutex Locks, and Message Queues",
functions: &[
("spawn(task_fn: fn) -> void", "Spawn a concurrent worker thread"),
("yield_now() -> void", "Yield execution to scheduler"),
],
},
StdlibModule {
name: "std::realtime",
description: "Deterministic Timers, Priority Scheduling & Deadline Monitoring",
functions: &[
("sleep_ms(ms: u32) -> void", "Sleep deterministic milliseconds without drift"),
("deadline_monitor(task_name: string, budget_ms: u32) -> bool", "Verify if current cycle is within deadline"),
("watchdog_reset() -> void", "Kick hardware watchdog timer"),
],
},
StdlibModule {
name: "std::kalman",
description: "Kalman Filter & Extended Kalman Filter (EKF) Sensor Fusion Equations",
functions: &[
("ekf_update(prior_x: f64, meas_z: f64, cov_p: f64) -> (f64, f64)", "1D/2D Extended Kalman Filter measurement step"),
("fuse_imu_gps(lat: f64, lon: f64, yaw: f64) -> (f64, f64)", "Fuse IMU orientation with GPS coordinates"),
],
},
StdlibModule {
name: "std::lidar",
description: "2D/3D LIDAR Scan Point Cloud Parsing, SLAM Scan Matching & Grid Map",
functions: &[
("scan_360(port: u32) -> [f64; 360]", "Acquire 360-degree distance scan from LIDAR sensor"),
("find_closest_obstacle(scan: [f64; 360]) -> (f64, u32)", "Return minimum distance and angle from scan"),
],
},
StdlibModule {
name: "std::kinematics",
description: "Inverse & Forward Kinematics Solvers for Robotic Arms & Wheels",
functions: &[
("ik_3dof_arm(x: f64, y: f64, z: f64) -> (f64, f64, f64)", "Compute joint angles for 3-DOF robotic manipulator"),
("diff_drive_ik(linear_v: f64, angular_w: f64) -> (f64, f64)", "Compute left and right wheel velocities"),
],
},
StdlibModule {
name: "std::path",
description: "Pure Pursuit Geometric Path Tracking & Curvature Guidance",
functions: &[
("pure_pursuit(rx: f64, ry: f64, rtheta: f64, wx: f64, wy: f64, v: f64) -> (f64, f64)", "Compute curvature kappa and angular velocity omega"),
],
},
StdlibModule {
name: "std::control",
description: "Advanced Controllers: LQR, Model Predictive Control (MPC), Feedforward PID",
functions: &[
("lqr_compute(err_x: f64, err_v: f64, k1: f64, k2: f64) -> f64", "Compute optimal Linear Quadratic Regulator feedback"),
("feedforward_pid(sp: f64, act: f64, kp: f64, ki: f64, kd: f64, kf: f64, prev_sp: f64, dt: f64) -> f64", "Compute Feed-Forward + PID output"),
("mpc_step(state: [f64; 4], horizon: u32) -> f64", "Compute 1-step Model Predictive Control action"),
],
},
StdlibModule {
name: "std::battery",
description: "Battery Management System (BMS), Voltage, SoC & Thermal Monitoring",
functions: &[
("read_soc() -> f64", "Read State of Charge percentage (0.0 to 100.0%)"),
("read_voltage() -> f64", "Read main pack voltage in Volts"),
("is_charging() -> bool", "Check if external charger is attached"),
],
},
StdlibModule {
name: "std::gps",
description: "GPS NMEA Sentence Parsing, Geodetic Coordinates & Waypoint Guidance",
functions: &[
("read_lat_lon() -> (f64, f64)", "Read current latitude and longitude degrees"),
("distance_to_waypoint(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64", "Haversine distance in meters"),
],
},
StdlibModule {
name: "std::servo",
description: "RC PWM Servo Angular Positioning & Trajectory Profiling",
functions: &[
("write_angle(pin: u32, degrees: f64) -> void", "Position servo motor from 0.0 to 180.0 degrees"),
("set_pulse_range(pin: u32, min_us: u32, max_us: u32) -> void", "Configure servo pulse width limits"),
],
},
StdlibModule {
name: "std::stepper",
description: "Stepper Motor Microstepping, Acceleration Ramps & Trapezoidal Profiler",
functions: &[
("step_angle(channel: u32, steps: i32, rpm: f64) -> void", "Move stepper motor specified steps at RPM"),
("set_microsteps(channel: u32, div: u32) -> void", "Configure microstep divisor (1, 2, 4, 8, 16, 32)"),
],
},
StdlibModule {
name: "std::watchdog",
description: "Hardware Watchdog Timer Control, Safety Dead-Man's Switch & Panic Recovery",
functions: &[
("start(timeout_ms: u32) -> void", "Start hardware watchdog timer"),
("reset() -> void", "Kick/refresh watchdog timer before expiration"),
("trigger_panic() -> void", "Trigger immediate MCU hardware safety reset"),
],
},
StdlibModule {
name: "std::canopen",
description: "Industrial CANopen DS301/DS402 Protocol for Smart Motors & Servos",
functions: &[
("send_pdo(node_id: u8, pdo_num: u8, data: [u8; 8]) -> void", "Transmit Process Data Object (PDO)"),
("write_sdo(node_id: u8, index: u16, sub: u8, val: u32) -> bool", "Write Service Data Object (SDO)"),
("nmt_command(node_id: u8, cmd: u8) -> void", "Send Network Management (NMT) state transition"),
],
},
StdlibModule {
name: "std::ethercat",
description: "High-Speed Real-Time EtherCAT Fieldbus for Industrial Arm Manipulators",
functions: &[
("init_master(iface: string) -> bool", "Initialize real-time EtherCAT master network"),
("sync_dc(cycle_time_ns: u32) -> void", "Synchronize Distributed Clocks (DC) across slaves"),
("write_pdo(slave_idx: u16, offset: u16, val: u32) -> void", "Write cyclic PDO process data"),
],
},
StdlibModule {
name: "std::modbus",
description: "Industrial Modbus RTU/TCP RS485 Protocol for PLC & Sensor Interfacing",
functions: &[
("read_holding_registers(slave: u8, addr: u16, count: u16) -> [u16; 16]", "Read Modbus holding registers"),
("write_coil(slave: u8, addr: u16, state: bool) -> bool", "Write single Modbus coil output"),
],
},
StdlibModule {
name: "std::slam",
description: "Autonomous SLAM 2D/3D Localization, Mapping & Occupancy Grid Engine",
functions: &[
("update_grid(scan: [f64; 360], pose_x: f64, pose_y: f64) -> void", "Update Bayesian occupancy grid map"),
("get_robot_pose() -> (f64, f64, f64)", "Get estimated map frame pose (X, Y, Theta)"),
("raycast_obstacle(angle: f64) -> f64", "Raycast expected obstacle distance in grid"),
],
},
StdlibModule {
name: "std::pid_autotune",
description: "Relay Autotuning Algorithm for Automated PID Gain Calibration",
functions: &[
("tune_step(actual: f64, setpoint: f64) -> f64", "Execute relay autotune excitation step"),
("get_gains_ziegler_nichols() -> (f64, f64, f64)", "Compute Ziegler-Nichols Kp, Ki, Kd gains"),
],
},
];
pub fn get_stdlib_module(name: &str) -> Option<&'static StdlibModule> {
STDLIB_MODULES.iter().find(|m| m.name == name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pid_computation() {
let out = pid_compute(100.0, 50.0, 1.5, 0.1, 0.05);
assert!(out > 0.0 && out <= 100.0);
}
#[test]
fn test_ekf_update() {
let (x, p) = ekf_update(10.0, 12.0, 0.5, 0.1);
assert!((x - 11.666).abs() < 0.1);
assert!(p < 0.5);
}
#[test]
fn test_ik_3dof_arm() {
let (base, shoulder, elbow) = ik_3dof_arm(150.0, 100.0, 200.0);
assert!(base >= 0.0 && base <= 180.0);
assert!(shoulder >= 0.0 && shoulder <= 180.0);
assert!(elbow >= 0.0 && elbow <= 180.0);
}
#[test]
fn test_diff_drive_ik() {
let (left, right) = diff_drive_ik(1.0, 0.5);
assert!(left > 0 && right > 0);
assert!(right > left); }
#[test]
fn test_haversine_distance() {
let d = haversine_distance(30.0, 31.0, 30.01, 31.01);
assert!(d > 1000.0 && d < 2000.0); }
#[test]
fn test_stateful_pid_damping() {
let out1 = pid_compute(100.0, 0.0, 1.0, 0.1, 0.05);
let out2 = pid_compute(100.0, 50.0, 1.0, 0.1, 0.05);
assert!(out2 < out1);
}
#[test]
fn test_pure_pursuit() {
let (kappa, w) = pure_pursuit(0.0, 0.0, 0.0, 10.0, 5.0, 1.5);
assert!(kappa > 0.0 && w > 0.0);
}
#[test]
fn test_feedforward_pid() {
let out = feedforward_pid(100.0, 90.0, 1.0, 0.01, 0.05, 0.5, 90.0, 0.005);
assert!(out > 0.0);
}
}