Created
July 17, 2019 14:23
-
-
Save Frans-Willem/d1ce1bfca14342c207da4973aadc80a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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