Skip to content

Commit

Permalink
Include dc_motor example, a simulation not in realtime.
Browse files Browse the repository at this point in the history
  • Loading branch information
gastmaier committed Aug 9, 2021
1 parent d5c958c commit cb143a3
Show file tree
Hide file tree
Showing 7 changed files with 347 additions and 50 deletions.
1 change: 1 addition & 0 deletions examples/dc_motor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This simulation is a simplification of gtoonstra/bldc-sim, made by Gerard Toonstra and available at https://github.com/gtoonstra/bldc-sim.
114 changes: 114 additions & 0 deletions examples/dc_motor/dc_motor_open.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
https://github.com/gtoonstra/bldc-sim

import utime
import math
from machine import Timer

class DCMotor(object):

def __init__ (
self,
Ra=0.52,
La=0.000036,
B=0.00001,
J=0.000012,
Kbemf=0.0137,
STATIC_FRICTION=0.01,
FRICTION_S=0.01
):
self.bemf = 0.0
# omega = rpm ( w )
self.omega = 0.0

# theta = electrical angle normalized to 2*pi
self.theta = 0.0

self.ia, self.va = 0.0, 0.0

self.Pelec, self.Te, self.Tl = 0.0, 0.0, 0.0

# La here is La subtracted by mutual inductance M.
self.Ra, self.La, self.B, self.J = Ra, La, B, J
self.Kbemf = Kbemf
self.STATIC_FRICTION, self.FRICTION_S = STATIC_FRICTION, FRICTION_S

self._last_time = 0.0


# The simulator
def sim( self, load, va, dt):
now = utime.ticks_us()

# Set the load
sign = math.copysign( 1, self.omega )
self.Tl = sign * load
self.va = va

# Calculate bemf
self.bemf = self.Kbemf * self.omega

# Calculate change in current per di/dt
dot_ia = (1.0 / self.La) * ( self.va - (self.Ra * self.ia) - self.bemf )

# Apply changes to current in phases
self.ia += dot_ia * dt

# Electrical torque. Since omega can be null, cannot derive from P/w
self.Te = self.Kbemf * self.ia

# Electrical power
self.Pelec = self.bemf * self.ia


# Mechanical torque.
# mtorque = ((etorque * (p->m->NbPoles / 2)) - (p->m->damping * sv->omega) - p->pv->torque);
self.Tm = ((self.Te) - (sign * self.B * abs(self.omega)) - self.Tl)

# Friction calculations
if abs(self.omega) < 1.0:
if abs(self.Te) < self.STATIC_FRICTION:
self.Tm = 0.0
else:
self.Tm -= self.STATIC_FRICTION
else:
self.Tm = self.Tm - sign * ( self.STATIC_FRICTION * math.exp( -5 * abs( self.omega )) + self.FRICTION_S )

# J is the moment of inertia
dotOmega = (self.Tm / self.J)
self.omega = self.omega + dotOmega * dt


self.theta += self.omega * dt
self.theta = self.theta % ( 2.0 * math.pi )

self._last_time += dt
return self.omega

def variables( self ):
ret = {}
ret[ "va" ] = self.va
ret[ "ia" ] = self.ia
ret[ "omega" ] = self.omega
ret[ "theta" ] = self.theta
ret[ "bemf" ] = self.bemf
ret[ "torque" ] = self.Te
ret[ "loadtorque" ] = self.Tl
return ret


dcmotor = DCMotor()
omega = dcmotor.omega
load = 0.3
step = 15
# how much time do you spend simulating
sim_time=10
#how much elapsed simulated time do you want to keep
print_interval=1000e-6
last_time=0

deadline4 = utime.ticks_add(utime.time(), sim_time)
while utime.ticks_diff(deadline4, utime.time()) > 0:
omega = dcmotor.sim(load, step, 32e-6)
if ((dcmotor._last_time - last_time) > print_interval):
print(dcmotor._last_time,",",omega)
last_time = dcmotor._last_time
117 changes: 117 additions & 0 deletions examples/dc_motor/dc_motor_pid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import utime
import math
from control import PID
from machine import Timer

class DCMotor(object):

def __init__ (
self,
Ra=0.52,
La=0.000036,
B=0.00001,
J=0.000012,
Kbemf=0.0137,
STATIC_FRICTION=0.01,
FRICTION_S=0.01
):
self.bemf = 0.0
# omega = rpm ( w )
self.omega = 0.0

# theta = electrical angle normalized to 2*pi
self.theta = 0.0

self.ia, self.va = 0.0, 0.0

self.Pelec, self.Te, self.Tl = 0.0, 0.0, 0.0

# La here is La subtracted by mutual inductance M.
self.Ra, self.La, self.B, self.J = Ra, La, B, J
self.Kbemf = Kbemf
self.STATIC_FRICTION, self.FRICTION_S = STATIC_FRICTION, FRICTION_S

self._last_time = 0.0


# The simulator
def sim( self, load, va, dt):
now = utime.ticks_us()

# Set the load
sign = math.copysign( 1, self.omega )
self.Tl = sign * load
self.va = va

# Calculate bemf
self.bemf = self.Kbemf * self.omega

# Calculate change in current per di/dt
dot_ia = (1.0 / self.La) * ( self.va - (self.Ra * self.ia) - self.bemf )

# Apply changes to current in phases
self.ia += dot_ia * dt

# Electrical torque. Since omega can be null, cannot derive from P/w
self.Te = self.Kbemf * self.ia

# Electrical power
self.Pelec = self.bemf * self.ia


# Mechanical torque.
# mtorque = ((etorque * (p->m->NbPoles / 2)) - (p->m->damping * sv->omega) - p->pv->torque);
self.Tm = ((self.Te) - (sign * self.B * abs(self.omega)) - self.Tl)

# Friction calculations
if abs(self.omega) < 1.0:
if abs(self.Te) < self.STATIC_FRICTION:
self.Tm = 0.0
else:
self.Tm -= self.STATIC_FRICTION
else:
self.Tm = self.Tm - sign * ( self.STATIC_FRICTION * math.exp( -5 * abs( self.omega )) + self.FRICTION_S )

# J is the moment of inertia
dotOmega = (self.Tm / self.J)
self.omega = self.omega + dotOmega * dt


self.theta += self.omega * dt
self.theta = self.theta % ( 2.0 * math.pi )

self._last_time += dt
return self.omega

def variables( self ):
ret = {}
ret[ "va" ] = self.va
ret[ "ia" ] = self.ia
ret[ "omega" ] = self.omega
ret[ "theta" ] = self.theta
ret[ "bemf" ] = self.bemf
ret[ "torque" ] = self.Te
ret[ "loadtorque" ] = self.Tl
return ret


dcmotor = DCMotor()
omega = dcmotor.omega
load = 0.3
step = 230
# how much time do you spend simulating
sim_time=10
#how much elapsed simulated time do you want to keep
print_interval=500e-6
power = 0
last_time=0

pid = PID(1.0634, 0.0082041, 0, setpoint=step)

deadline4 = utime.ticks_add(utime.time(), sim_time)
while utime.ticks_diff(deadline4, utime.time()) > 0:
omega = dcmotor.sim(load, power, 32e-6)
power = pid(omega, 32e-6)
if ((dcmotor._last_time - last_time) > print_interval):
print(dcmotor._last_time,",",omega,",",pid.setpoint,",",power)
last_time = dcmotor._last_time
48 changes: 0 additions & 48 deletions examples/water_boiler/water_boiler.py

This file was deleted.

55 changes: 55 additions & 0 deletions examples/water_boiler/water_boiler_open.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import utime
from machine import Timer


class WaterBoiler:
"""
Simple simulation of a water boiler which can heat up water
and where the heat dissipates slowly over time
"""

def __init__(self, dissipation=0.2):
self.water_temp = 20
self.ambient = 20
self.dissipation = dissipation
self._last_time = utime.ticks_ms()

def update(self, boiler_power):
now = utime.ticks_ms()
dt = utime.ticks_diff(now,self._last_time) if (utime.ticks_diff(now,self._last_time)) else 1e-16
if boiler_power > 0:
# Boiler can only produce heat, not cold
self.water_temp += 1 * boiler_power * dt / 1000

# Some heat dissipation
self.water_temp -= (self.water_temp - self.ambient) * self.dissipation * dt

self._last_time = now
return self.water_temp

boiler = WaterBoiler(0.001)
water_temp = boiler.water_temp
step = 40

#Timer Function Callback
def timerFunc0(t):
global water_temp
# step of 20
water_temp = boiler.update(step)

def timerFunc1(t):
global water_temp
print(boiler._last_time,",",water_temp)

def timerFunc2(t):
tim0.deinit()
tim1.deinit()


tim0=Timer(0)
tim0.init(period=100, mode=Timer.PERIODIC, callback=timerFunc0)
tim1=Timer(1)
tim1.init(period=200, mode=Timer.PERIODIC, callback=timerFunc1)
tim2=Timer(2)
tim2.init(period=10000, mode=Timer.ONE_SHOT, callback=timerFunc2)

58 changes: 58 additions & 0 deletions examples/water_boiler/water_boiler_pid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import utime
from PID import PID
from machine import Timer


class WaterBoiler:
"""
Simple simulation of a water boiler which can heat up water
and where the heat dissipates slowly over time
"""

def __init__(self, dissipation=0.2):
self.water_temp = 20
self.ambient = 20
self.dissipation = dissipation
self._last_time = utime.ticks_ms()

def update(self, boiler_power):
now = utime.ticks_ms()
dt = utime.ticks_diff(now,self._last_time) if (utime.ticks_diff(now,self._last_time)) else 1e-16
if boiler_power > 0:
# Boiler can only produce heat, not cold
self.water_temp += 1 * boiler_power * dt / 1000

# Some heat dissipation that depend on the difference of temperature
self.water_temp -= (self.water_temp - self.ambient) * self.dissipation * dt

self._last_time = now
return self.water_temp

boiler = WaterBoiler(0.001)
power = 0
water_temp = boiler.water_temp
pid = PID(3.7293, 0.9540, 0, setpoint=51, sample_time=200, scale='ms')
pid.output_limits = (0, 100)

#Timer Function Callback
def timerFunc0(t):
global power, water_temp
water_temp = boiler.update(power)
power = pid(water_temp)

def timerFunc1(t):
global power, water_temp
print(pid._last_time,",",water_temp,",",pid.setpoint,",",power)

def timerFunc2(t):
tim0.deinit()
tim1.deinit()


tim0=Timer(0)
tim0.init(period=100, mode=Timer.PERIODIC, callback=timerFunc0)
tim1=Timer(1)
tim1.init(period=200, mode=Timer.PERIODIC, callback=timerFunc1)
tim2=Timer(2)
tim2.init(period=10000, mode=Timer.ONE_SHOT, callback=timerFunc2)

Loading

0 comments on commit cb143a3

Please sign in to comment.