Skip to content

Commit cb143a3

Browse files
committed
Include dc_motor example, a simulation not in realtime.
1 parent d5c958c commit cb143a3

File tree

7 files changed

+347
-50
lines changed

7 files changed

+347
-50
lines changed

examples/dc_motor/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This simulation is a simplification of gtoonstra/bldc-sim, made by Gerard Toonstra and available at https://github.com/gtoonstra/bldc-sim.

examples/dc_motor/dc_motor_open.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
https://github.com/gtoonstra/bldc-sim
2+
3+
import utime
4+
import math
5+
from machine import Timer
6+
7+
class DCMotor(object):
8+
9+
def __init__ (
10+
self,
11+
Ra=0.52,
12+
La=0.000036,
13+
B=0.00001,
14+
J=0.000012,
15+
Kbemf=0.0137,
16+
STATIC_FRICTION=0.01,
17+
FRICTION_S=0.01
18+
):
19+
self.bemf = 0.0
20+
# omega = rpm ( w )
21+
self.omega = 0.0
22+
23+
# theta = electrical angle normalized to 2*pi
24+
self.theta = 0.0
25+
26+
self.ia, self.va = 0.0, 0.0
27+
28+
self.Pelec, self.Te, self.Tl = 0.0, 0.0, 0.0
29+
30+
# La here is La subtracted by mutual inductance M.
31+
self.Ra, self.La, self.B, self.J = Ra, La, B, J
32+
self.Kbemf = Kbemf
33+
self.STATIC_FRICTION, self.FRICTION_S = STATIC_FRICTION, FRICTION_S
34+
35+
self._last_time = 0.0
36+
37+
38+
# The simulator
39+
def sim( self, load, va, dt):
40+
now = utime.ticks_us()
41+
42+
# Set the load
43+
sign = math.copysign( 1, self.omega )
44+
self.Tl = sign * load
45+
self.va = va
46+
47+
# Calculate bemf
48+
self.bemf = self.Kbemf * self.omega
49+
50+
# Calculate change in current per di/dt
51+
dot_ia = (1.0 / self.La) * ( self.va - (self.Ra * self.ia) - self.bemf )
52+
53+
# Apply changes to current in phases
54+
self.ia += dot_ia * dt
55+
56+
# Electrical torque. Since omega can be null, cannot derive from P/w
57+
self.Te = self.Kbemf * self.ia
58+
59+
# Electrical power
60+
self.Pelec = self.bemf * self.ia
61+
62+
63+
# Mechanical torque.
64+
# mtorque = ((etorque * (p->m->NbPoles / 2)) - (p->m->damping * sv->omega) - p->pv->torque);
65+
self.Tm = ((self.Te) - (sign * self.B * abs(self.omega)) - self.Tl)
66+
67+
# Friction calculations
68+
if abs(self.omega) < 1.0:
69+
if abs(self.Te) < self.STATIC_FRICTION:
70+
self.Tm = 0.0
71+
else:
72+
self.Tm -= self.STATIC_FRICTION
73+
else:
74+
self.Tm = self.Tm - sign * ( self.STATIC_FRICTION * math.exp( -5 * abs( self.omega )) + self.FRICTION_S )
75+
76+
# J is the moment of inertia
77+
dotOmega = (self.Tm / self.J)
78+
self.omega = self.omega + dotOmega * dt
79+
80+
81+
self.theta += self.omega * dt
82+
self.theta = self.theta % ( 2.0 * math.pi )
83+
84+
self._last_time += dt
85+
return self.omega
86+
87+
def variables( self ):
88+
ret = {}
89+
ret[ "va" ] = self.va
90+
ret[ "ia" ] = self.ia
91+
ret[ "omega" ] = self.omega
92+
ret[ "theta" ] = self.theta
93+
ret[ "bemf" ] = self.bemf
94+
ret[ "torque" ] = self.Te
95+
ret[ "loadtorque" ] = self.Tl
96+
return ret
97+
98+
99+
dcmotor = DCMotor()
100+
omega = dcmotor.omega
101+
load = 0.3
102+
step = 15
103+
# how much time do you spend simulating
104+
sim_time=10
105+
#how much elapsed simulated time do you want to keep
106+
print_interval=1000e-6
107+
last_time=0
108+
109+
deadline4 = utime.ticks_add(utime.time(), sim_time)
110+
while utime.ticks_diff(deadline4, utime.time()) > 0:
111+
omega = dcmotor.sim(load, step, 32e-6)
112+
if ((dcmotor._last_time - last_time) > print_interval):
113+
print(dcmotor._last_time,",",omega)
114+
last_time = dcmotor._last_time

examples/dc_motor/dc_motor_pid.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import utime
2+
import math
3+
from control import PID
4+
from machine import Timer
5+
6+
class DCMotor(object):
7+
8+
def __init__ (
9+
self,
10+
Ra=0.52,
11+
La=0.000036,
12+
B=0.00001,
13+
J=0.000012,
14+
Kbemf=0.0137,
15+
STATIC_FRICTION=0.01,
16+
FRICTION_S=0.01
17+
):
18+
self.bemf = 0.0
19+
# omega = rpm ( w )
20+
self.omega = 0.0
21+
22+
# theta = electrical angle normalized to 2*pi
23+
self.theta = 0.0
24+
25+
self.ia, self.va = 0.0, 0.0
26+
27+
self.Pelec, self.Te, self.Tl = 0.0, 0.0, 0.0
28+
29+
# La here is La subtracted by mutual inductance M.
30+
self.Ra, self.La, self.B, self.J = Ra, La, B, J
31+
self.Kbemf = Kbemf
32+
self.STATIC_FRICTION, self.FRICTION_S = STATIC_FRICTION, FRICTION_S
33+
34+
self._last_time = 0.0
35+
36+
37+
# The simulator
38+
def sim( self, load, va, dt):
39+
now = utime.ticks_us()
40+
41+
# Set the load
42+
sign = math.copysign( 1, self.omega )
43+
self.Tl = sign * load
44+
self.va = va
45+
46+
# Calculate bemf
47+
self.bemf = self.Kbemf * self.omega
48+
49+
# Calculate change in current per di/dt
50+
dot_ia = (1.0 / self.La) * ( self.va - (self.Ra * self.ia) - self.bemf )
51+
52+
# Apply changes to current in phases
53+
self.ia += dot_ia * dt
54+
55+
# Electrical torque. Since omega can be null, cannot derive from P/w
56+
self.Te = self.Kbemf * self.ia
57+
58+
# Electrical power
59+
self.Pelec = self.bemf * self.ia
60+
61+
62+
# Mechanical torque.
63+
# mtorque = ((etorque * (p->m->NbPoles / 2)) - (p->m->damping * sv->omega) - p->pv->torque);
64+
self.Tm = ((self.Te) - (sign * self.B * abs(self.omega)) - self.Tl)
65+
66+
# Friction calculations
67+
if abs(self.omega) < 1.0:
68+
if abs(self.Te) < self.STATIC_FRICTION:
69+
self.Tm = 0.0
70+
else:
71+
self.Tm -= self.STATIC_FRICTION
72+
else:
73+
self.Tm = self.Tm - sign * ( self.STATIC_FRICTION * math.exp( -5 * abs( self.omega )) + self.FRICTION_S )
74+
75+
# J is the moment of inertia
76+
dotOmega = (self.Tm / self.J)
77+
self.omega = self.omega + dotOmega * dt
78+
79+
80+
self.theta += self.omega * dt
81+
self.theta = self.theta % ( 2.0 * math.pi )
82+
83+
self._last_time += dt
84+
return self.omega
85+
86+
def variables( self ):
87+
ret = {}
88+
ret[ "va" ] = self.va
89+
ret[ "ia" ] = self.ia
90+
ret[ "omega" ] = self.omega
91+
ret[ "theta" ] = self.theta
92+
ret[ "bemf" ] = self.bemf
93+
ret[ "torque" ] = self.Te
94+
ret[ "loadtorque" ] = self.Tl
95+
return ret
96+
97+
98+
dcmotor = DCMotor()
99+
omega = dcmotor.omega
100+
load = 0.3
101+
step = 230
102+
# how much time do you spend simulating
103+
sim_time=10
104+
#how much elapsed simulated time do you want to keep
105+
print_interval=500e-6
106+
power = 0
107+
last_time=0
108+
109+
pid = PID(1.0634, 0.0082041, 0, setpoint=step)
110+
111+
deadline4 = utime.ticks_add(utime.time(), sim_time)
112+
while utime.ticks_diff(deadline4, utime.time()) > 0:
113+
omega = dcmotor.sim(load, power, 32e-6)
114+
power = pid(omega, 32e-6)
115+
if ((dcmotor._last_time - last_time) > print_interval):
116+
print(dcmotor._last_time,",",omega,",",pid.setpoint,",",power)
117+
last_time = dcmotor._last_time

examples/water_boiler/water_boiler.py

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import utime
2+
from machine import Timer
3+
4+
5+
class WaterBoiler:
6+
"""
7+
Simple simulation of a water boiler which can heat up water
8+
and where the heat dissipates slowly over time
9+
"""
10+
11+
def __init__(self, dissipation=0.2):
12+
self.water_temp = 20
13+
self.ambient = 20
14+
self.dissipation = dissipation
15+
self._last_time = utime.ticks_ms()
16+
17+
def update(self, boiler_power):
18+
now = utime.ticks_ms()
19+
dt = utime.ticks_diff(now,self._last_time) if (utime.ticks_diff(now,self._last_time)) else 1e-16
20+
if boiler_power > 0:
21+
# Boiler can only produce heat, not cold
22+
self.water_temp += 1 * boiler_power * dt / 1000
23+
24+
# Some heat dissipation
25+
self.water_temp -= (self.water_temp - self.ambient) * self.dissipation * dt
26+
27+
self._last_time = now
28+
return self.water_temp
29+
30+
boiler = WaterBoiler(0.001)
31+
water_temp = boiler.water_temp
32+
step = 40
33+
34+
#Timer Function Callback
35+
def timerFunc0(t):
36+
global water_temp
37+
# step of 20
38+
water_temp = boiler.update(step)
39+
40+
def timerFunc1(t):
41+
global water_temp
42+
print(boiler._last_time,",",water_temp)
43+
44+
def timerFunc2(t):
45+
tim0.deinit()
46+
tim1.deinit()
47+
48+
49+
tim0=Timer(0)
50+
tim0.init(period=100, mode=Timer.PERIODIC, callback=timerFunc0)
51+
tim1=Timer(1)
52+
tim1.init(period=200, mode=Timer.PERIODIC, callback=timerFunc1)
53+
tim2=Timer(2)
54+
tim2.init(period=10000, mode=Timer.ONE_SHOT, callback=timerFunc2)
55+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import utime
2+
from PID import PID
3+
from machine import Timer
4+
5+
6+
class WaterBoiler:
7+
"""
8+
Simple simulation of a water boiler which can heat up water
9+
and where the heat dissipates slowly over time
10+
"""
11+
12+
def __init__(self, dissipation=0.2):
13+
self.water_temp = 20
14+
self.ambient = 20
15+
self.dissipation = dissipation
16+
self._last_time = utime.ticks_ms()
17+
18+
def update(self, boiler_power):
19+
now = utime.ticks_ms()
20+
dt = utime.ticks_diff(now,self._last_time) if (utime.ticks_diff(now,self._last_time)) else 1e-16
21+
if boiler_power > 0:
22+
# Boiler can only produce heat, not cold
23+
self.water_temp += 1 * boiler_power * dt / 1000
24+
25+
# Some heat dissipation that depend on the difference of temperature
26+
self.water_temp -= (self.water_temp - self.ambient) * self.dissipation * dt
27+
28+
self._last_time = now
29+
return self.water_temp
30+
31+
boiler = WaterBoiler(0.001)
32+
power = 0
33+
water_temp = boiler.water_temp
34+
pid = PID(3.7293, 0.9540, 0, setpoint=51, sample_time=200, scale='ms')
35+
pid.output_limits = (0, 100)
36+
37+
#Timer Function Callback
38+
def timerFunc0(t):
39+
global power, water_temp
40+
water_temp = boiler.update(power)
41+
power = pid(water_temp)
42+
43+
def timerFunc1(t):
44+
global power, water_temp
45+
print(pid._last_time,",",water_temp,",",pid.setpoint,",",power)
46+
47+
def timerFunc2(t):
48+
tim0.deinit()
49+
tim1.deinit()
50+
51+
52+
tim0=Timer(0)
53+
tim0.init(period=100, mode=Timer.PERIODIC, callback=timerFunc0)
54+
tim1=Timer(1)
55+
tim1.init(period=200, mode=Timer.PERIODIC, callback=timerFunc1)
56+
tim2=Timer(2)
57+
tim2.init(period=10000, mode=Timer.ONE_SHOT, callback=timerFunc2)
58+

0 commit comments

Comments
 (0)