A Rust repository focused on numerical simulation and visualization of: partial differential equations (PDEs) and ordinary differential equations (ODEs). The project includes two complete, end-to-end examples:
- PDE: 2D heat-conduction equation solved with an explicit finite-difference method (FTCS), generating heatmap snapshots and summary plots via Plotters.
- ODE: a nonlinear cart–pole (inverted pendulum) system stabilized using Sliding Mode Control (SMC), with high-FPS frame rendering to PNG via image + imageproc, plots via Plotters, and optional MP4 encoding using FFmpeg.
Build system: Cargo • Plotting: Plotters • Frame rendering: image + imageproc • Optional video encoding: FFmpeg
- About this repository
- What are ODEs and PDEs?
- Example 1 (PDE): 2D Heat Equation
- Example 2 (ODE): Inverted Pendulum with Sliding Mode Control
- Sliding Mode Control (SMC) theory
- Numerical methods used in this repo
- Plots and animations (Plotters, imageproc, FFmpeg)
- Dependencies and installation
- Building the project
- Running the executables and generating results
- Repository file guide (full explanation)
- Operating system guides (Windows / macOS / Linux)
- Troubleshooting
- Implementation tutorial video
This repository is designed as a practical reference for implementing numerical solvers in modern Rust:
- A PDE workflow that starts from a physical model (heat conduction), discretizes it, runs a time-marching solver, and produces professional plots and image snapshots.
- An ODE + control workflow that implements a nonlinear plant (cart–pole), injects disturbances, runs a robust controller (Sliding Mode Control), generates simulation plots, and produces an animation/video.
The goal is not just “making it work,” but also illustrating common engineering requirements:
- stable time stepping (CFL / explicit stability constraints),
- clean logging to CSV for reproducibility,
- high-quality plot generation for analysis,
- animations for communication and debugging,
- portable builds via Cargo (and simple CI if you add GitHub Actions later).
heat2d→ solves the 2D heat equation and writes results intooutput/heat2d/pendulum_sliding_mode→ simulates a cart–pole and writes results intooutput/pendulum_sliding_mode/
An ODE describes the evolution of one or more state variables with respect to a single independent variable (usually time):
where:
-
$\mathbf{x}(t)$ is the state (e.g., cart position, pole angle), -
$\mathbf{u}(t)$ is an input (e.g., a control force), -
$\mathbf{f}(\cdot)$ describes the system dynamics.
In this repository, the inverted pendulum example is a nonlinear ODE system integrated in time using an RK4 scheme.
A PDE involves partial derivatives with respect to two or more independent variables (e.g., time and space). A canonical example is the heat equation (diffusion equation):
where:
-
$T(x,y,t)$ is temperature, -
$\alpha$ is thermal diffusivity, -
$(x,y)$ are spatial coordinates, -
$t$ is time.
In this repository, the heat equation is discretized in space using finite differences and integrated forward in time using an explicit FTCS method.
The PDE solved in src/heat2d/main.rs is:
Interpretation:
- conduction in a 2D plate,
- no internal heat sources,
- constant isotropic diffusivity
$\alpha$ , - fixed temperature boundaries (Dirichlet BCs).
The implemented boundary conditions are:
- left boundary held at
$T=100$ , - right/top/bottom held at
$T=0$ ,
which produces a diffusion front moving from the left edge into the interior.
The solver uses:
- uniform grid in
$x$ and$y$ , - second-order central differences for
$\partial^2 T / \partial x^2$ and$\partial^2 T / \partial y^2$ , - explicit time stepping (Forward Euler in time).
At interior grid nodes
(See the source file for the exact discrete update used.)
The explicit 2D diffusion scheme requires a stability constraint on
The code computes this value (as dt_stable) and uses a conservative factor (0.80) to avoid running near the limit.
When you run heat2d, you will find:
- multiple heatmap snapshots:
output/heat2d/heat_t*.png - final centerline profile:
output/heat2d/centerline_final.png - center temperature vs time:
output/heat2d/center_point_vs_time.png - CSV log:
output/heat2d/heat2d_log.csv
This combination is typical for PDE workflows:
- images for qualitative verification,
- plots for engineering interpretation,
- CSV for reproducibility and post-processing in Python/Matlab/Excel.
The second example (src/pendulum_sliding_mode/main.rs) simulates a nonlinear cart–pole system with disturbances and stabilizes it using Sliding Mode Control (SMC).
The code uses the following state definition:
-
$x$ is the cart position (meters), -
$\theta$ is the pole angle (radians) with$\theta=0$ being upright, - sign convention is chosen such that
$\theta > 0$ visually leans the pole to the right.
Two practical helper operations are used:
- angle wrapping into
$[-\pi,\pi]$ to avoid numeric drift, - clamping/saturation for control and gating logic.
The example injects two disturbance pulses (implemented as an external torque about the pole pivot):
- pulse 1: starts at
$t=0.5,s$ , positive torque (“push right”) - pulse 2: starts at
$t=5.0,s$ , negative torque (“push left”)
Each pulse uses a smooth half-sine profile over a 0.5 second duration, and the disturbance arrow is shown for 0.5 seconds only.
Disturbance torque model:
where
For visualization and logging, the code also reports an “equivalent bob force”:
This example is intentionally “complete”:
- Integrate the nonlinear dynamics (RK4)
- Render each frame using the current integrated state (CPU rasterization)
- Save frames to disk
- Optionally encode a video using FFmpeg
- Generate Plotters plots
- Save a CSV log for analysis
Outputs include:
output/pendulum_sliding_mode/frames/frame_000000.png... etc.output/pendulum_sliding_mode/cartpole_log.csv- multiple plots: cart position, pole angle, control force, disturbance torque, sliding surface, etc.
- MP4 video (if FFmpeg is available)
Important note about frame count: the source code sets total_frames = 1800 (which corresponds to 180 FPS for a 10s animation). If you want 6000 frames / 600 FPS exactly, update:
const TOTAL_FRAMES: usize = 6000;- and (optionally) rename the MP4 output filename accordingly.
Sliding Mode Control is a robust nonlinear control method designed to enforce a chosen “sliding manifold” (sliding surface) in the state space, even in the presence of disturbances and model uncertainties.
For a general second-order system, a common sliding surface is:
where
In this repository, the “error” is the inverted pendulum’s deviation from upright (
Interpretation:
-
$\lambda_{\theta}$ defines how strongly the controller penalizes angle error, -
$\alpha$ couples cart motion to the sliding surface (cart movement is necessary to catch the pendulum), -
$\lambda_x$ adds a “soft” cart-centering effect as part of the surface.
A classical sufficient condition for reaching and maintaining the sliding manifold is the Lyapunov inequality:
with
A typical control law to satisfy this is:
where
The ideal sign function can cause chattering (high-frequency switching) in practice. A common mitigation is to replace sign$(s)$ with a continuous saturation function:
and use:
where
This repository implements exactly this idea, with parameters:
k(gain)phi(boundary layer)
The plant dynamics are nonlinear and do not provide a simple closed-form affine relationship between the cart force
The code therefore uses a pragmatic engineering approach:
- Define the desired sliding surface derivative:
$\dot{s}_{des} = -k,\mathrm{sat}\left(\frac{s}{\phi}\right).$ - Approximate
$\dot{s}(u)$ locally as an affine function:$\dot{s}(u) \approx a,u + b.$ - Estimate
$a$ and$b$ numerically using two evaluations of the nominal dynamics (disturbance ignored in the control law):$a \approx \dot{s}(1) - \dot{s}(0), \quad b \approx \dot{s}(0).$ - Solve for the control:
$u_{smc} = \frac{\dot{s}_{des} - b}{a}.$ - Saturate the actuator:
$u = \mathrm{clamp}(u_{smc} + u_{hold}, -u_{max}, u_{max}).$
A practical issue in inverted pendulum stabilization is the cart drifting away from the origin. If we always apply a centering controller, it can interfere with the fast “catching” action needed during large angle errors.
This repository uses a gated cart-centering term:
where:
-
$g(\theta) \in [0,1]$ , -
$g(\theta) \approx 1$ when$|\theta|$ is small (near upright), -
$g(\theta) \approx 0$ when$|\theta|$ is large (prioritize stabilization/catch maneuver).
This is implemented by:
Result: the controller focuses on balancing first, then recenters the cart when it is safe.
The heat solver is a textbook example of an explicit diffusion integrator:
- Space: central difference (2nd order)
- Time: forward Euler (1st order)
Strengths:
- straightforward to implement,
- fast per-step computation,
- easy to parallelize later.
Limitations:
- stability constraint can force small
$\Delta t$ for fine grids, - accuracy is limited by explicit time stepping for stiff diffusion regimes.
A natural extension (not yet implemented here) is an implicit method (e.g., Crank–Nicolson or ADI) to remove/relax stability constraints.
The cart–pole uses classical Runge–Kutta 4th order (RK4). The simulation renders frames at high FPS, so the code uses substeps per frame:
- choose a frame time-step
$\Delta t_{frame}$ based on desired animation duration and frame count, - integrate dynamics with smaller steps
$\Delta t_{physics} = \Delta t_{frame}/N_{substeps}$ .
This is a common workflow for physically plausible animations:
- stable dynamics integration,
- deterministic frame output,
- tight coupling between simulation and visualization.
This repository uses Plotters for plotting. Plotters is a pure-Rust plotting library that can write images directly without an external plotting backend.
Typical outputs generated with Plotters in this repo:
- line plots (time histories, cross-sections),
- heatmaps saved as PNG images,
- diagnostic plots (sliding surface, disturbance signals, control signals).
Plot quality: plots are written at high resolution (large pixel dimensions) and configured with:
- large font sizes (titles, axes, tick labels),
- thicker lines for readability,
- bounded tick counts (≤ 10 labels per axis),
- English tick formatting with one decimal digit.
The inverted pendulum example renders frames by drawing primitives into a bitmap:
- background, track, cart, wheels
- a rotated pole (as a filled quadrilateral)
- bob at the pole tip
- a constant-length disturbance arrow (only visible for 0.5 seconds after each disturbance start)
This approach is deterministic and portable because it does not rely on a GPU or window system.
If FFmpeg is installed and available in PATH, the pendulum program will attempt to encode an MP4 automatically.
If FFmpeg is not available, the program still generates PNG frames, and you can encode the video manually (see OS-specific sections).
- Rust toolchain (stable):
rustc,cargo - A C toolchain is not required for the core code, but Windows users should install “Build Tools for Visual Studio” because many Rust crates ship native code in dependencies.
- FFmpeg (required only if you want MP4 encoding)
- All Rust dependencies are pulled automatically by Cargo.
- There is no external runtime plotting backend required.
A typical Release build:
cargo build --releaseCargo places outputs under:
target/release/
Using Cargo:
cargo run --release --bin heat2dOr run the compiled executable directly:
# Windows
.\target\release\heat2d.exe
# Linux/macOS
./target/release/heat2dResults appear in:
output/heat2d/
Using Cargo:
cargo run --release --bin pendulum_sliding_modeOptional preview window (still saves frames):
cargo run --release --bin pendulum_sliding_mode -- --previewResults appear in:
output/pendulum_sliding_mode/- frames in
output/pendulum_sliding_mode/frames/
This section explains every important file in the repository and its role.
Key responsibilities:
- declares crate dependencies:
- Plotters for plots and heatmaps
- image + imageproc for frame rendering
- csv for logging
- minifb (optional preview window)
- declares two binaries:
heat2dfromsrc/heat2d/main.rspendulum_sliding_modefromsrc/pendulum_sliding_mode/main.rs
This is the full PDE pipeline:
- defines a 2D domain with uniform grid (
nx,ny), - applies Dirichlet boundary conditions (fixed temperatures),
- computes a stable explicit time-step constraint and chooses a conservative
dt, - iterates over time steps:
- updates interior nodes via FTCS
- reapplies boundary conditions
- periodically saves heatmap snapshots
- logs the center temperature over time and saves:
- a centerline plot at final time
- a time-history plot
- a CSV log (
heat2d_log.csv)
Engineering notes that are easy to miss but important:
- the 2D grid is flattened into a 1D vector for speed and cache efficiency,
- a single indexing helper encodes 2D indexing,
- snapshots are intentionally throttled to avoid producing thousands of images,
- plots are written at high resolution with controlled tick density.
This is the most comprehensive file in the repository: it contains modeling, control, integration, rendering, logging, plotting, and optional video encoding.
Main components:
- Plant model (cart–pole):
PoleModelcomputes center-of-mass and inertia parameters for a rod + bob mass.CartPoleParamsstores masses, gravity, and damping.- A dynamics function computes state derivatives given control force and external torque.
- Numerical integration:
- RK4 is used for time integration.
- Multiple physics steps per rendered frame increase stability and realism.
- Controller:
- A sliding surface
sis defined. - The control uses a boundary-layer saturation and adds a gated cart-centering term.
- Actuator saturation prevents unrealistic forces.
- Disturbance injection:
- Two torque pulses occur at fixed times.
- The “equivalent bob force” drives the direction of the on-screen arrow.
- Rendering and output:
- CPU rasterization draws cart, pole, bob, and arrows into an offscreen buffer.
- Frames are saved as PNG images.
- Optional
--previewshows the animation while frames are generated.
- Plots and CSV logging:
- Plotters saves time histories for position, angle, control input, disturbance, and sliding surface.
- CSV is written for offline analysis.
- FFmpeg encoding:
- The program attempts to call FFmpeg automatically if it is present on PATH.
- Manual encoding is also supported (see OS sections).
This project targets the MSVC Rust toolchain on Windows. The most reliable approach is to build from the Developer PowerShell for VS 2022 (or the x64 Native Tools Command Prompt), because it pre-configures the MSVC compiler + linker environment.
- Install prerequisites:
- Rust (Rustup) using the
x86_64-pc-windows-msvctoolchain - Visual Studio 2022 Build Tools with the workload: Desktop development with C++
- Windows 10/11 SDK (installed with the Build Tools when selected)
-
Open: Developer PowerShell for VS 2022.
-
Build and run:
cd path\to\ODE_PDE_RUST
cargo build --release
cargo run --release --bin heat2d
cargo run --release --bin pendulum_sliding_modeSome Windows environments (especially when MSYS2 / Git Bash is installed) can accidentally pick up a non-MSVC link.exe.
If you see errors referencing paths like C:\msys64\usr\bin\link.exe, you can explicitly configure the MSVC libraries and linker
inside PowerShell before building.
Run the following PowerShell commands (edit the version/path to match your machine):
# 1) MSVC version you have (taken from your MSVC folder name)
# Change this to match your system.
# How to find it:
# Open: C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\
# Pick the newest folder name (example: 14.44.35207)
$msvcVer = "14.44.35207"
# 2) MSVC lib directory (x64)
# This is the MSVC toolchain library directory used by the linker.
# If you installed Visual Studio in a different edition/location, you may need to adjust the base path.
$msvcLib = "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\$msvcVer\lib\x64"
# 3) Windows SDK (Windows Kits) library directories
# The script below auto-selects the newest installed SDK version under:
# C:\Program Files (x86)\Windows Kits\10\Lib\
# If your Windows Kits is installed elsewhere, update $sdkRoot.
$sdkRoot = "C:\Program Files (x86)\Windows Kits\10\Lib"
$sdkVer = (Get-ChildItem $sdkRoot -Directory | Sort-Object Name -Descending | Select-Object -First 1).Name
# These two are required for Windows system libraries:
# - um\x64 (contains kernel32.lib, user32.lib, etc.)
# - ucrt\x64 (contains Universal CRT libraries)
$umLib = Join-Path $sdkRoot "$sdkVer\um\x64"
$ucrtLib = Join-Path $sdkRoot "$sdkVer\ucrt\x64"
# 4) Sanity checks
# These should print: True / True
# If either prints False, your installation is missing components or paths are wrong.
Test-Path (Join-Path $umLib "kernel32.lib")
Test-Path (Join-Path $msvcLib "msvcrt.lib")
# 5) Tell the linker where to find .lib files (critical on Windows)
# We prepend MSVC + Windows SDK library paths to the LIB environment variable.
# This helps avoid "kernel32.lib not found" and related linker errors.
$env:LIB = "$msvcLib;$ucrtLib;$umLib;$env:LIB"
# 6) Force Cargo to use MSVC's linker (NOT MSYS2/Git's link.exe)
# Change this path if your MSVC version differs or if Visual Studio is installed elsewhere.
$env:CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER = "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\link.exe"
# 7) Build and run from the repository root (where Cargo.toml is)
# Update this path to your clone location.
cd "$env:USERPROFILE\Desktop\ODE_PDE_Rust-main\ODE_PDE_Rust-main"
# Clean (optional) then build and run the binaries.
cargo clean
cargo build --release
cargo run --release --bin heat2d
cargo run --release --bin pendulum_sliding_mode
- Install Rust (or verify it is installed):
rustc --version
cargo --version- Optional FFmpeg:
brew install ffmpeg- Run:
cargo run --release --bin heat2d
cargo run --release --bin pendulum_sliding_mode- Install Rust (or verify it is installed):
rustc --version
cargo --version- Optional FFmpeg:
sudo apt-get update
sudo apt-get install -y ffmpeg- Run:
cargo run --release --bin heat2d
cargo run --release --bin pendulum_sliding_mode- Install “Build Tools for Visual Studio” (C/C++ toolchain).
- Open a new terminal after installation.
- The program only creates an MP4 if
ffmpegis available on PATH. - If auto-encoding fails, encode manually using the provided commands in the OS sections.
- The pendulum example saves many PNGs; disk IO can dominate runtime.
- Reduce
TOTAL_FRAMESif you want faster runs. - Use
--releaseto ensure optimized output. - Consider using a faster SSD.
- Plots are saved at high pixel resolutions; zoom in or open at full resolution.
- If you need even larger fonts, adjust the font sizes in the plotting helper functions in the source code.
At the end of the workflow, you can watch the full implementation and walkthrough on YouTube.