Skip to content

Instantly share code, notes, and snippets.

@Frans-Willem
Created July 17, 2019 14:23
Show Gist options
  • Save Frans-Willem/d1ce1bfca14342c207da4973aadc80a3 to your computer and use it in GitHub Desktop.
Save Frans-Willem/d1ce1bfca14342c207da4973aadc80a3 to your computer and use it in GitHub Desktop.
import math
import numpy as np
# Current table in code, to plot alongside newly calculated
current_svm_table = [
239 ,
241 ,
242 ,
243 ,
245 ,
246 ,
247 ,
248 ,
249 ,
250 ,
251 ,
251 ,
252 ,
253 ,
253 ,
254 ,
254 ,
254 ,
255 ,
255 ,
255 ,
255 ,
255 ,
255 ,
254 ,
254 ,
254 ,
253 ,
253 ,
252 ,
251 ,
250 ,
250 ,
249 ,
248 ,
247 ,
245 ,
244 ,
243 ,
242 ,
240 ,
239 ,
236 ,
231 ,
227 ,
222 ,
217 ,
212 ,
207 ,
202 ,
197 ,
191 ,
186 ,
181 ,
176 ,
170 ,
165 ,
160 ,
154 ,
149 ,
144 ,
138 ,
133 ,
127 ,
122 ,
116 ,
111 ,
106 ,
100 ,
95 ,
89 ,
84 ,
79 ,
74 ,
68 ,
63 ,
58 ,
53 ,
48 ,
43 ,
38 ,
33 ,
28 ,
23 ,
18 ,
16 ,
14 ,
13 ,
12 ,
10 ,
9 ,
8 ,
7 ,
6 ,
5 ,
4 ,
3 ,
3 ,
2 ,
1 ,
1 ,
1 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
1 ,
1 ,
2 ,
2 ,
3 ,
4 ,
5 ,
6 ,
6 ,
8 ,
9 ,
10 ,
11 ,
12 ,
14 ,
15 ,
17 ,
15 ,
14 ,
12 ,
11 ,
10 ,
9 ,
8 ,
6 ,
6 ,
5 ,
4 ,
3 ,
2 ,
2 ,
1 ,
1 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
0 ,
1 ,
1 ,
1 ,
2 ,
3 ,
3 ,
4 ,
5 ,
6 ,
7 ,
8 ,
9 ,
10 ,
12 ,
13 ,
14 ,
16 ,
18 ,
23 ,
28 ,
33 ,
38 ,
43 ,
48 ,
53 ,
58 ,
63 ,
68 ,
74 ,
79 ,
84 ,
89 ,
95 ,
100 ,
106 ,
111 ,
116 ,
122 ,
127 ,
133 ,
138 ,
144 ,
149 ,
154 ,
160 ,
165 ,
170 ,
176 ,
181 ,
186 ,
191 ,
197 ,
202 ,
207 ,
212 ,
217 ,
222 ,
227 ,
231 ,
236 ,
239 ,
240 ,
242 ,
243 ,
244 ,
245 ,
247 ,
248 ,
249 ,
250 ,
250 ,
251 ,
252 ,
253 ,
253 ,
254 ,
254 ,
254 ,
255 ,
255 ,
255 ,
255 ,
255 ,
255 ,
254 ,
254 ,
254 ,
253 ,
253 ,
252 ,
251 ,
251 ,
250 ,
249 ,
248 ,
247 ,
246 ,
245 ,
243 ,
242 ,
241 ,
239 ,
238 ,
]
# Used in calculation
MINIMUM_PWM_PERIOD = 0
MAXIMUM_PWM_PERIOD = 255
# From degrees, get a unit vector in that direction.
def unitvector_from_degrees(deg):
rad = math.radians(deg)
return np.array([[math.sin(rad)], [math.cos(rad)]])
# Takes an input vector Vinput,
# two basis vectors Va and Vb,
# and outputs two scalars a and b,
# such that Vinput = a*Va + b*Vb
def change_of_basis(Vinput, Va, Vb):
# Vinput = [Va | Vb] * [a, b]
# [Va | Vb]^-1 * Vinput = [a,b]
VaVb = np.concatenate([Va,Vb], axis=1)
VaVbinv = np.linalg.inv(VaVb)
result = VaVbinv.dot(Vinput)
return (result[0][0], result[1][0])
# Our PWM scheme allows us to make one phase negative for a period of time,
# and one phase positive for another,
# but one has to remain zero.
# A valid basis thus has at most one negative component,
# and one positive component.
def is_valid_basis(abc):
pos, neg = (0,0)
for component in abc:
if component < 0:
neg = neg + 1
elif component > 0:
pos = pos + 1
return neg<=1 and pos<=1
# Find a valid basis such that there is at most one positive,
# and at most one negative component
def find_valid_basis(Vref, Va, Vb, Vc):
a, b = change_of_basis(Vref, Va, Vb)
if is_valid_basis([a,b,0]):
return [a,b,0]
b,c = change_of_basis(Vref, Vb, Vc)
if is_valid_basis([0, b, c]):
return [0, b, c]
c, a = change_of_basis(Vref, Vc, Va)
if is_valid_basis([a, 0, c]):
return [a, 0, c]
return None
# Given that from our desired rotation,
# we've found a basis from the vectors of our motor coils
# that has one negative, one positive, and one zero component,
# Generate the PWM numbers for these phases.
# Note that this PWM signal still has one phase going to zero immediately,
# and the unit of the duration is not specified, and may exceed zero.
def calculate_pwm(abc):
sorted_phases = sorted(zip(abc, range(0, 3)))
period_accumulated = 0
output = [0,0,0]
for (period, phase) in sorted_phases:
if period > 0:
period_accumulated += period
output[phase] = period_accumulated
if period < 0:
period_accumulated -= period
return output
# For an index, calculate a valid phase basis (see is_valid_basis)
def calculate_basis(index):
# Convert index 0-256 to degrees 0-360
# Using 0-256 'degrees' means we can use standard uint8_t overflow,
# instead of having to do an explicit modulo operation.
degrees = (index / 256) * 360
# Calculate a unit vector in the direction of degrees, this is the resulting vector that we'd like
Vref = unitvector_from_degrees(degrees)
# Calculate vectors in the directions of our A, B, C poles
Va = unitvector_from_degrees(0)
Vb = unitvector_from_degrees(120)
Vc = unitvector_from_degrees(240)
return find_valid_basis(Vref, Va, Vb, Vc)
# Given three PWM periods that are uncapped,
# Cap them to the min_new_period and max_new_period,
# And in the process also center and round them.
def map_pwm(periods, min_period, max_period, min_new_period, max_new_period):
# First map them from 0 to 1
periods = tuple((period - min_period)/(max_period-min_period) for period in periods)
# Then center them
max_period_distance = max(periods)-min(periods)
periods = tuple(period + (1-max_period_distance)/2 for period in periods)
# Map to min-max, and round
periods = tuple(int(round(period * (max_new_period-min_new_period) + min_new_period)) for period in periods)
return periods
basis = map(calculate_basis, range(0, 256))
uncapped_pwm = list(map(calculate_pwm, basis))
maximum_uncapped_pwm_period = max(map(max, uncapped_pwm))
results = map(lambda pwms: map_pwm(pwms, 0, maximum_uncapped_pwm_period, MINIMUM_PWM_PERIOD, MAXIMUM_PWM_PERIOD), uncapped_pwm)
results = list(results)
import matplotlib.pyplot as plt
# Plot first component of calculated results
plt.plot(list(map(lambda pwm: pwm[0], results)))
# Plot current table in source
plt.plot(current_svm_table)
plt.ylabel("PWM")
plt.xlabel("8-bit degrees")
plt.legend(["Calculated", "Current"])
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment