-
Notifications
You must be signed in to change notification settings - Fork 0
/
motor.py
127 lines (106 loc) · 5.17 KB
/
motor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import pigpio
import math
import time
from profiler import profile
class Motor:
def __init__(self, gpio):
self.gpio = gpio
self.position = 0 #steps
self.velocity = 0 #steps/s
self.acceleration = 12000.0 #steps/s2
self.maxVelocity = 6000.0 #steps/s
pigpio.pi().set_mode(self.gpio, pigpio.OUTPUT)
class Integrator:
def __init__(self, motor):
self.motor = motor
self.pulseDuration = 100
self.commandQueue = []
self.currentCommandStep = 0
self.lastCommand = {"pos" : 0, "vel": 0, "acc": 0}
self.pulseBuffer = []
self.pulseBufferLoopable = False
def g00(self, pos):
print("New Movement to", pos)
#Get ramp up, steady and ramp down segments
self.commandQueue.extend(self.calculateTrapezoidalProfile(pos - self.motor.position, 0, 0))
@profile
def integrate(self, steps = -1):
if len(self.commandQueue) == 0:
return []
#array where we store pulses to send
if steps < self.commandQueue[0].position - self.currentCommandStep:
if self.pulseBufferLoopable and self.commandQueue[0].acceleration == 0:
print("Reusing pulseBuffer")
self.currentCommandStep+=steps
return self.pulseBuffer
#If we didn't reuse the last pulseBuffer, let's see if we can reuse the next one
self.pulseBufferLoopable = True
self.pulseBuffer = []
while steps > 0 and len(self.commandQueue) > 0:
#print ("Command","Pos", self.commandQueue[0].position,"Acc", self.commandQueue[0].acceleration, self.currentCommandStep)
stepsForCommand = int(min(steps, self.commandQueue[0].position - self.currentCommandStep))
if(stepsForCommand > 0):
steps -= stepsForCommand
self.integrateSteps(stepsForCommand , self.pulseBuffer)
if(stepsForCommand == 0):
#We changed commands during this pulseBuffer, so it's not loopable
self.pulseBufferLoopable = False
self.currentCommandStep = 0
self.commandQueue.pop(0)
if len(self.commandQueue) == 0:
return self.pulseBuffer
self.lastCommand["pos"] = self.commandQueue[0].position
self.lastCommand["acc"] = self.commandQueue[0].acceleration
#f = open('motor.csv', 'w')
#f.close() # you can omit in most cases as the destructor will call it
return self.pulseBuffer
@profile
def integrateSteps(self, steps, pulseBuffer):
#We calculate nextTime in an iterative way
#nextTime for step 0
print("Integrating", steps)
c0 = math.sqrt(2/self.motor.acceleration)
for s in xrange(int(steps)):
if(self.commandQueue[0].acceleration != 0):
if(self.commandQueue[0].acceleration >= 0):
accelFactor = self.currentCommandStep
else:
accelFactor = self.commandQueue[0].position - self.currentCommandStep
nextTime = c0 * ( math.sqrt(accelFactor + 1) - math.sqrt(accelFactor) )
self.motor.velocity = 1 / nextTime
else:
if(self.motor.velocity == 0):
#if not accelerating and speed = 0, we can't move!
return pulseBuffer
nextTime = 1 / self.motor.velocity
self.motor.position += 1
self.currentCommandStep+= 1
#print("Next",nextTime,"V", self.motor.velocity,"P", self.motor.position)
#csvLine = (str(totalTime) + ";" + str(self.motor.velocity) +"\n").replace('.', ',')
#f.write(csvLine) # python will convert \n to os.linesep
nextTime = math.floor(nextTime * 1000000)
pulseBuffer.append(pigpio.pulse(1<<self.motor.gpio, 0, self.pulseDuration))
pulseBuffer.append(pigpio.pulse(0, 1<<self.motor.gpio, nextTime - self.pulseDuration))
def calculateTrapezoidalProfile(self, deltaPos, vIni, vEnd):
commands = []
#This method takes a motion command and splits it into two or three commands
#for trapezoidal or triangular speed profile
#v' = v + a*t --> t = (v' - v) / a
timeToMaxSpeed = (self.motor.maxVelocity - vIni) / self.motor.acceleration
print("Time to Max Speed", timeToMaxSpeed)
distanceToMaxSpeed = vIni * timeToMaxSpeed + 0.5 * self.motor.acceleration * math.pow(timeToMaxSpeed, 2)
print("Distance to Max Speed", distanceToMaxSpeed)
if distanceToMaxSpeed >= (deltaPos / 2):
#Triangular profile
commands.append(Command(deltaPos/2, self.motor.acceleration))
commands.append(Command(deltaPos/2, -self.motor.acceleration))
else:
#Trapezoidal profile
commands.append(Command(distanceToMaxSpeed, self.motor.acceleration))
commands.append(Command(deltaPos - 2*distanceToMaxSpeed, 0))
commands.append(Command(distanceToMaxSpeed, -self.motor.acceleration))
return commands
class Command:
def __init__(self, p, a):
self.position = p
self.acceleration = a