Programming Cosmic Dynamics in a Robot Car through variable inertia with NKTg Law
Nguyen.Khanh Tung 2026-04-19 18:02:12 17 Views0 Replies I'd like to share a robotics project that takes a different approach to motor control — instead of using PID, the robot uses a physics law called NKTg Law to decide when to brake, decelerate, or run normally.
🧪 The Core Idea
Classical physics treats mass as constant. But real robots change mass constantly — picking up cargo, dropping it, consuming battery. The NKTg Law by Nguyen Khanh Tung handles this directly:
NKTg = f(x, v, m)
NKTg₁ = x × m × v → Is the robot moving away from stable state?NKTg₂ = (dm/dt) × m × v → Is mass change affecting momentum?This law was verified against real ESA Mars orbital data with only 0.208% error. Now it runs on an ESP32.
🛒 Parts List (~$55–$65 total)
| ESP32 DevKit V1 | ~$6 |
| MPU6050 IMU | ~$2 |
| Encoder 600PPR × 2 | ~$6.50/each |
| Load Cell 5kg + HX711 | ~$5 |
| Motor Driver BTS7960 | ~$6 |
| DC Gear Motor 12V × 2 + wheels | ~$6/set |
| Li-ion 3S 11.1V 3000mAh | ~$10 |
| Robot chassis 2WD/4WD | ~$6–$12 |
🔌 Wiring
MPU6050 → ESP32: SDA→GPIO21, SCL→GPIO22 HX711 → ESP32: DT→GPIO4, SCK→GPIO5 Encoder L→ ESP32: A→GPIO34, B→GPIO35 Encoder R→ ESP32: A→GPIO36, B→GPIO39 BTS7960 → ESP32: RPWM→GPIO25, LPWM→GPIO26, EN→GPIO27 BTS7960 → Battery: 12V direct
⚙️ How Motor Control Works
The ESP32 runs 3 layers at 100Hz:
Layer 1 — Read sensors:
Encoder → position x (m) and velocity v (m/s)Load Cell → mass m (kg) and dm/dt (kg/s)MPU6050 → acceleration cross-checkLayer 2 — Compute NKTg:
cpp
float nktg1(float x, float v, float m) { return x * (m * v); } float nktg2(float dm_dt, float v, float m) { return dm_dt * (m * v); }
Layer 3 — Drive motors:
cpp
if (n1 > 500.0) → emergency brake // unstable else if (n2 < -50.0) → slow down 50% // sudden mass loss else → normal run // stable
Layer 4 — Web dashboard at 20Hz: ESP32 streams x, v, m, dm/dt via USB Serial → open NKTg_System.html in Chrome/Edge → real-time charts.
📋 Full ESP32 Code
cpp
#include <Wire.h> #include <MPU6050.h> #include <HX711.h> MPU6050 imu; HX711 loadcell; #define ENC_L_A 34 #define ENC_L_B 35 #define MOTOR_RPWM 25 #define MOTOR_LPWM 26 #define MOTOR_EN 27 #define HX711_DT 4 #define HX711_SCK 5 volatile long encoderCount = 0; float x = 0, v = 0, m_prev = 0, m_curr = 0, dm_dt = 0; const float WHEEL_CIRCUMFERENCE = 0.2; const int PPR = 600; const float LOADCELL_SCALE = 2280.0; const unsigned long DT = 10; const unsigned long SEND_INTERVAL = 50; unsigned long lastTime = 0, lastSend = 0; void IRAM_ATTR encoderISR() { if (digitalRead(ENC_L_B) > 0) encoderCount++; else encoderCount--; } float nktg1(float x, float v, float m) { return x * (m * v); } float nktg2(float dm_dt, float v, float m) { return dm_dt * (m * v); } void driveMotor(float n1, float n2) { if (n1 > 500.0) { ledcWrite(0, 0); ledcWrite(1, 0); } else if (n2 < -50.0) { ledcWrite(0, 90); ledcWrite(1, 0); } else { ledcWrite(0, 180); ledcWrite(1, 0); } } void setup() { Serial.begin(115200); Wire.begin(); imu.initialize(); loadcell.begin(HX711_DT, HX711_SCK); loadcell.set_scale(LOADCELL_SCALE); loadcell.tare(); pinMode(ENC_L_A, INPUT_PULLUP); pinMode(ENC_L_B, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(ENC_L_A), encoderISR, RISING); ledcSetup(0, 5000, 8); ledcSetup(1, 5000, 8); ledcAttachPin(MOTOR_RPWM, 0); ledcAttachPin(MOTOR_LPWM, 1); pinMode(MOTOR_EN, OUTPUT); digitalWrite(MOTOR_EN, HIGH); m_curr = loadcell.get_units(5); m_prev = m_curr; lastTime = millis(); Serial.println("READY"); } void loop() { unsigned long now = millis(); if (now - lastTime >= DT) { float dt_sec = (now - lastTime) / 1000.0; lastTime = now; long count = encoderCount; encoderCount = 0; float distance = (float)count / PPR * WHEEL_CIRCUMFERENCE; v = distance / dt_sec; x += distance; if (loadcell.is_ready()) { m_prev = m_curr; m_curr = max(0.1f, loadcell.get_units()); dm_dt = (m_curr - m_prev) / dt_sec; } driveMotor(nktg1(x, v, m_curr), nktg2(dm_dt, v, m_curr)); } if (now - lastSend >= SEND_INTERVAL) { lastSend = now; Serial.print(x, 3); Serial.print(","); Serial.print(v, 3); Serial.print(","); Serial.print(m_curr, 3); Serial.print(","); Serial.println(dm_dt, 4); } }
🎛️ Calibration Tips
Load Cell: Place a 1kg weight → adjust LOADCELL_SCALE until reading = 1.000 kgWheel: Measure actual circumference → update WHEEL_CIRCUMFERENCEThresholds: Start with 500 / −50, tune for your hardware📖 Want to go deeper? (Drones, UAVs, Space Navigation)
This robot is just one application. The full book "Programming Cosmic Dynamics" covers NKTg across 11 programming languages — from Python to Assembly to Quantum Computing — with real ESA data validation.
Get the book:
🔹 Leanpub: https://leanpub.com/NKTgLaw (EN) | https://leanpub.com/NKTgLaw-vi (VI)🔹 Google Play: https://play.google.com/store/books/details?id=CNLKEQAAQBAJ&pli🔹 Amazon: https://www.amazon.com/dp/B0GTSBBRYRResources:
🌐 Live dashboard: https://traiphieu.com/nktg_system/💻 GitHub (150+ languages): https://github.com/NKTgLaw/NKTgLaw📄 DOI: 10.5281/zenodo.15808498💬 Questions for the community
Has anyone tried replacing PID with a physics-based controller on ESP32?What sensors do you use to measure real-time mass changes on your robots?Would NKTg₂ (mass-momentum exchange) be useful for your drone or delivery robot projects?Looking forward to your thoughts! 🚀
— Nguyen Khanh Tung | ORCID: 0009-0002-9877-4137 | traiphieu.com

