да, я тоже об этом думал, наверное для каждого локомотива придется индивидуально подбирать
я тут кстати спросил у китайского чатика про реалистичное управление, вот что он мне выдал
Here’s the **complete MicroPython code** for the **Raspberry Pi Pico W**, using a **DRV8871 motor driver**, **rotary encoder**, and **INA219 current sensor** (since the INA3221 isn't as common with Pico).
---
### **Features**
✅ **Encoder-controlled PWM** (smooth acceleration/deceleration)
✅ **INA219 current monitoring** (detect load/stall)
✅ **Simulated inertia** (heavier acceleration at low speeds, slower at high speeds)
✅ **Dynamic braking** (faster deceleration when throttle is zero)
✅ **Non-linear speed curve** (exponential easing for realism)
---
### **Hardware Setup**
- **Raspberry Pi Pico W**
- **DRV8871 Motor Driver** (controls motor direction & PWM)
- **INA219 Current Sensor** (monitors motor load)
- **Rotary Encoder** (sets target speed)
- **DC Motor** (in your model train)
#### **Wiring**
| Pico W | DRV8871 | INA219 | Encoder |
|-------------|---------|--------|---------|
| GP16 (PWM) | IN1 | - | - |
| GP17 (DIR) | IN2 | - | - |
| GP0 (SDA) | - | SDA | - |
| GP1 (SCL) | - | SCL | - |
| GP14 (A0) | - | - | CLK |
| GP15 (A1) | - | - | DT |
---
### **Full MicroPython Code**
```python
from machine import Pin, PWM, I2C, Timer
import time
from ina219 import INA219 # Requires `micropython-ina219` library
# Pins
PWM_PIN = 16
DIR_PIN = 17
ENC_A = 14
ENC_B = 15
# Motor & Physics Params
MAX_PWM = 65535 # Pico PWM is 16-bit (0-65535)
MIN_PWM = 10000 # Minimum PWM to overcome friction
ACCEL_RATE = 0.5 # Base acceleration rate
DECEL_RATE = 1.2 # Braking is faster than acceleration
INERTIA_FACTOR = 0.05 # Higher = more "mass"
# Current Sensing (INA219)
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
ina = INA219(i2c, addr=0x40) # Default I2C address
ina.configure() # Default settings
# Encoder Variables
encoder_pos = 0
last_enc_pos = 0
target_pwm = 0
# Motor Control Variables
current_pwm = 0
motor_current = 0
load_compensation = 0
# Initialize Motor PWM
pwm = PWM(Pin(PWM_PIN))
pwm.freq(1000) # 1kHz PWM frequency
dir_pin = Pin(DIR_PIN, Pin.OUT)
dir_pin.value(1) # Default direction (forward)
# Encoder ISR
def update_encoder(pin):
global encoder_pos
if enc_a.value() == enc_b.value():
encoder_pos += 1
else:
encoder_pos -= 1
# Setup Encoder Pins
enc_a = Pin(ENC_A, Pin.IN, Pin.PULL_UP)
enc_b = Pin(ENC_B, Pin.IN, Pin.PULL_UP)
enc_a.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=update_encoder)
# ================= MAIN LOOP =================
def update_motor(timer):
global current_pwm, last_enc_pos, motor_current, load_compensation
# Read Encoder (Set Target Speed)
enc_change = encoder_pos - last_enc_pos
if enc_change != 0:
target_pwm = max(0, min(MAX_PWM, target_pwm + enc_change * 1000)) # Scale encoder steps
last_enc_pos = encoder_pos
print("Target PWM:", target_pwm)
# Read Motor Current (INA219)
motor_current = ina.current() # mA
print("Current:", motor_current, "mA")
# Load Compensation (Increase PWM if struggling)
if motor_current > 500 and current_pwm < MAX_PWM - 20000: # Arbitrary threshold
load_compensation = 5000 # Slight boost
else:
load_compensation = 0
# Realistic Acceleration/Braking
# Calculate Inertia (Slower accel at high speeds)
inertia = 1.0 - (current_pwm / MAX_PWM) * INERTIA_FACTOR
# Acceleration
if current_pwm < target_pwm:
current_pwm += (ACCEL_RATE * inertia * 1000) + load_compensation
if current_pwm > target_pwm:
current_pwm = target_pwm
# Braking (Faster deceleration when throttle is zero)
elif current_pwm > target_pwm:
rate = DECEL_RATE if target_pwm == 0 else ACCEL_RATE
current_pwm -= (rate * inertia * 1000)
if current_pwm < target_pwm:
current_pwm = target_pwm
# Apply Minimum PWM (to overcome friction)
output_pwm = max(MIN_PWM, int(current_pwm)) if target_pwm > 0 else int(current_pwm)
output_pwm = max(0, min(MAX_PWM, output_pwm))
# Set Motor PWM
pwm.duty_u16(output_pwm)
print("Output PWM:", output_pwm)
# Run motor update every 10ms
timer = Timer()
timer.init(period=10, mode=Timer.PERIODIC, callback=update_motor)
# Keep program running
while True:
time.sleep(1)
```
---
### **Key Adjustments for Realism**
1. **`ACCEL_RATE` / `DECEL_RATE`**
- Increase for faster response, decrease for smoother transitions.
2. **`INERTIA_FACTOR`**
- Higher = more "massive" train (slower acceleration at high speeds).
3. **`load_compensation`**
- Adjust threshold (`motor_current > 500`) based on your motor’s stall current.
4. **`MIN_PWM`**
- Set to the lowest PWM value that moves your train (avoids stalling).
---
### **How It Works**
1. **Encoder Input** → Sets `target_pwm`.
2. **INA219 Current Sensing** → Detects load (e.g., uphill) and compensates.
3. **Inertia Simulation** → Acceleration slows at higher speeds.
4. **Dynamic Braking** → Faster deceleration when throttle is zero.
---
### **Required Libraries**
- **`micropython-ina219`** (for current sensing):
```bash
mpremote mip install github:chrisb2/micropython-ina219
```
---
### **Next Steps**
- **Tune parameters** for your specific train.
- **Add a direction toggle** (flip `DIR_PIN` based on a button).
- **Log data** (e.g., plot `current_pwm` vs. `motor_current` in Thonny).
Would you like any modifications (e.g., adding a PID controller for even smoother control)?