rename to levSim, requirements, readme
112
ReadMe.md
@@ -1,6 +1,112 @@
|
|||||||
# Levitation Control Repository
|
# Levitation Control Repository
|
||||||
This Repo contains the code and data from sensor calibration as well as arduino code for 4-point control on the 2-yoke test rig in the ```PIDTesting-2yoke4coil``` directory.
|
|
||||||
|
|
||||||
This code is property of Texas Guadaloop, a student-led hyperloop team from the University of Texas at Austin.
|
This repository contains embedded firmware, sensor characterization tooling, and a physics-based simulation stack as well as control algorithm trials for the Texas Guadaloop maglev system.
|
||||||
|
|
||||||
To be able to run all python files in the repo, run ```pip install -r requirements.txt```
|
This code is property of **Texas Guadaloop**, a student-led hyperloop team from the University of Texas at Austin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── AltSensorTesting/ # Arduino firmware for Baumer inductive sensor characterization
|
||||||
|
├── sensor/ # Python scripts to collect and fit sensor calibration data
|
||||||
|
├── loadCellCode/ # Arduino firmware for HX711 load-cell calibration
|
||||||
|
├── TwoCellMagChar/ # Two-cell magnetic characterization rig (validates Ansys data)
|
||||||
|
├── lev_sim/ # PyBullet simulation driven by Ansys sweep data
|
||||||
|
├── MAGLEV_DIGITALTWIN_PYTHON/ # Earlier single-axis analytical digital twin (reference)
|
||||||
|
├── serial_plotter.py # Live serial plotter utility
|
||||||
|
├── equilibrium.py # Equilibrium gap/current solver
|
||||||
|
└── requirements.txt # Python dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Embedded Stack
|
||||||
|
|
||||||
|
### `AltSensorTesting/`
|
||||||
|
|
||||||
|
Arduino firmware for reading Baumer inductive analog distance sensors. Uses interrupt-driven ADC sampling at ~77 kHz (16 MHz / prescaler 16) for low-latency gap measurement. Tracks the 10 lowest and 10 highest in-range ADC values over a sampling window to help establish calibration bounds. An out-of-range (OOR) digital pin is monitored in the ISR to discard invalid readings.
|
||||||
|
|
||||||
|
Key details:
|
||||||
|
- ADC ISR at ~77 kHz; readings discarded automatically when OOR pin is HIGH
|
||||||
|
- Serial commands: `1` to start sampling, `0` to stop and print boundary statistics
|
||||||
|
- Baud rate: 2,000,000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sensor Characterization
|
||||||
|
|
||||||
|
### `sensor/`
|
||||||
|
|
||||||
|
Python pipeline for converting raw ADC readings from the inductive gap sensors into calibrated millimeter distances.
|
||||||
|
|
||||||
|
- **`sensorCollector.py`** — Serial interface that reads live ADC values from Arduino and applies the calibration model in real time.
|
||||||
|
- **`analogFitter-*.py`** — Curve-fitting scripts (polynomial, exponential, 3/4/5-parameter logistic) that fit calibration sweep data (`data*.csv`) to find the best sensor model. The 5-parameter generalized logistic form was found to give the best fit.
|
||||||
|
- **`Sensor*Averages.csv` / `data*.csv`** — Raw and averaged calibration data for sensors 0–3. Sensor 3 required a different voltage divider (20 kΩ / 50 kΩ) because the induction sensor output exceeds 6 V.
|
||||||
|
|
||||||
|
Calibration constants (A, K, B, C, v) for the generalized logistic model are embedded directly in `sensorCollector.py` and `sensor_simplerNew.py` for deployment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Magnetic Characterization (`TwoCellMagChar/`)
|
||||||
|
|
||||||
|
A minimal bench rig used to validate Ansys Maxwell FEA force predictions experimentally.
|
||||||
|
|
||||||
|
- **`TwoCellMagChar.ino`** — Drives two H-bridge coil channels across a sweep of PWM values (−250 to +250 in steps of 50) while reading two HX711 load cells simultaneously. Averages 10 measurements per PWM step and reports gram-force readings over serial.
|
||||||
|
- **`MagCharTrial.xlsx`** — Recorded force-vs-PWM data from physical trials.
|
||||||
|
- **`CalibConsts.hpp`** — Load-cell offset and scale constants shared with `loadCellCode/`.
|
||||||
|
|
||||||
|
The measured force curves confirmed that the Ansys sweep data is in reasonable agreement with physical hardware, providing confidence for using the FEA model inside the simulation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Levitation Simulation (`lev_sim/`)
|
||||||
|
|
||||||
|
A PyBullet-based simulation environment for 4-point active magnetic levitation of the pod. The simulation is driven by an Ansys Maxwell parametric sweep (coil currents × gap height × roll angle → force & torque), fitted to a polynomial regression model for fast inference.
|
||||||
|
|
||||||
|
### Data pipeline
|
||||||
|
|
||||||
|
1. **Ansys sweep** — `Ansys Results 12-9.csv / .xlsx` contains FEA results sweeping left/right coil currents, roll angle, and gap height.
|
||||||
|
2. **Function Fitting** — `Function Fitting.ipynb` fits the Ansys data to a `PolynomialFeatures + LinearRegression` model (inputs: `currL`, `currR`, `roll`, `1/gap`). The trained model is saved to `maglev_model.pkl`.
|
||||||
|
3. **Fast inference** — `maglev_predictor.py` (`MaglevPredictor`) loads the pickle and bypasses sklearn overhead by extracting raw weight matrices, running pure-NumPy polynomial expansion for ~100× faster per-sample prediction.
|
||||||
|
|
||||||
|
### Simulation environment
|
||||||
|
|
||||||
|
`lev_pod_env.py` implements a [Gymnasium](https://gymnasium.farama.org/) `Env` wrapping PyBullet:
|
||||||
|
|
||||||
|
- **State**: 4 gap heights (normalized) + 4 gap-height velocities
|
||||||
|
- **Action**: 4 PWM duty cycles ∈ [−1, 1] (front-left, front-right, back-left, back-right coils)
|
||||||
|
- **Physics**: first-order RL circuit model per coil (`mag_lev_coil.py`); coil parameters: R = 1.1 Ω, L = 2.5 mH, V_supply = 12 V, I_max = 10.2 A
|
||||||
|
- **Forces**: `MaglevPredictor.predict()` maps coil currents + gap + roll → force and torque applied to the pod body in PyBullet
|
||||||
|
- **Noise**: configurable Gaussian sensor noise (default σ = 0.1 mm) and stochastic disturbance forces
|
||||||
|
- Target equilibrium gap: 11.86 mm (9.4 kg pod)
|
||||||
|
- Simulation timestep: 1/240 s
|
||||||
|
|
||||||
|
The environment is controller-agnostic — any control algorithm can be plugged in:
|
||||||
|
|
||||||
|
| File | Controller |
|
||||||
|
|------|-----------|
|
||||||
|
| `pid_simulation.py` | Feedforward LUT + PID |
|
||||||
|
| `lev_PID.ipynb` | Interactive PID tuning notebook |
|
||||||
|
| `optuna_pid_tune.py` | Optuna-based automated PID hyperparameter search |
|
||||||
|
| `lev_PPO.ipynb` | PPO reinforcement learning via Stable-Baselines3 |
|
||||||
|
|
||||||
|
Pre-tuned PID gain sets are saved in `pid_best_params*.json` (variants for 1500, 3000, and 6000 Optuna trials).
|
||||||
|
|
||||||
|
### PWM circuit model
|
||||||
|
|
||||||
|
`PWM_Circuit_Model.py` is a standalone electrical model of the H-bridge PWM drive circuit used for offline verification of the coil current dynamics.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Key dependencies: `pybullet`, `gymnasium`, `stable-baselines3`, `scikit-learn`, `optuna`, `pyserial`, `numpy`, `scipy`, `pandas`, `matplotlib`.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 221 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 159 KiB |
1032
lev_sim/lev_PID.ipynb
Normal file
67
requirements.txt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
alembic==1.18.4
|
||||||
|
appnope==0.1.4
|
||||||
|
asttokens==3.0.1
|
||||||
|
cloudpickle==3.1.2
|
||||||
|
colorlog==6.10.1
|
||||||
|
comm==0.2.3
|
||||||
|
contourpy==1.3.3
|
||||||
|
cycler==0.12.1
|
||||||
|
debugpy==1.8.20
|
||||||
|
decorator==5.2.1
|
||||||
|
executing==2.2.1
|
||||||
|
Farama-Notifications==0.0.4
|
||||||
|
filelock==3.25.2
|
||||||
|
fonttools==4.61.1
|
||||||
|
fsspec==2026.2.0
|
||||||
|
gymnasium==1.2.3
|
||||||
|
ImageIO==2.37.2
|
||||||
|
imageio-ffmpeg==0.6.0
|
||||||
|
ipykernel==7.2.0
|
||||||
|
ipython==9.10.0
|
||||||
|
ipython_pygments_lexers==1.1.1
|
||||||
|
jedi==0.19.2
|
||||||
|
Jinja2==3.1.6
|
||||||
|
joblib==1.5.3
|
||||||
|
jupyter_client==8.8.0
|
||||||
|
jupyter_core==5.9.1
|
||||||
|
kiwisolver==1.4.9
|
||||||
|
Mako==1.3.10
|
||||||
|
MarkupSafe==3.0.3
|
||||||
|
matplotlib==3.10.8
|
||||||
|
matplotlib-inline==0.2.1
|
||||||
|
mpmath==1.3.0
|
||||||
|
nest-asyncio==1.6.0
|
||||||
|
networkx==3.6.1
|
||||||
|
numpy==2.4.2
|
||||||
|
optuna==4.7.0
|
||||||
|
packaging==26.0
|
||||||
|
pandas==3.0.1
|
||||||
|
parso==0.8.6
|
||||||
|
pexpect==4.9.0
|
||||||
|
pillow==12.1.1
|
||||||
|
platformdirs==4.9.2
|
||||||
|
prompt_toolkit==3.0.52
|
||||||
|
psutil==7.2.2
|
||||||
|
ptyprocess==0.7.0
|
||||||
|
pure_eval==0.2.3
|
||||||
|
pybullet==3.2.7
|
||||||
|
Pygments==2.19.2
|
||||||
|
pyparsing==3.3.2
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
PyYAML==6.0.3
|
||||||
|
pyzmq==27.1.0
|
||||||
|
scikit-learn==1.8.0
|
||||||
|
scipy==1.17.0
|
||||||
|
seaborn==0.13.2
|
||||||
|
setuptools==82.0.1
|
||||||
|
six==1.17.0
|
||||||
|
SQLAlchemy==2.0.46
|
||||||
|
stack-data==0.6.3
|
||||||
|
sympy==1.14.0
|
||||||
|
threadpoolctl==3.6.0
|
||||||
|
torch==2.10.0
|
||||||
|
tornado==6.5.4
|
||||||
|
tqdm==4.67.3
|
||||||
|
traitlets==5.14.3
|
||||||
|
typing_extensions==4.15.0
|
||||||
|
wcwidth==0.6.0
|
||||||