2424import config
2525import logging
2626import sonar
27-
2827import mpu
2928
3029PIN_MOTOR_ENABLE = 22
4746PWM_FREQUENCY = 100 #Hz
4847PWM_RANGE = 100 #0-100
4948
50- def coderbot_callback (gpio , level , tick ):
51- return CoderBot .get_instance ().callback (gpio , level , tick )
52-
5349class CoderBot (object ):
5450 _pin_out = [PIN_MOTOR_ENABLE , PIN_LEFT_FORWARD , PIN_RIGHT_FORWARD , PIN_LEFT_BACKWARD , PIN_RIGHT_BACKWARD , PIN_SERVO_3 , PIN_SERVO_4 ]
5551
5652 def __init__ (self , servo = False , motor_trim_factor = 1.0 ):
5753 self .pi = pigpio .pi ('localhost' )
5854 self .pi .set_mode (PIN_PUSHBUTTON , pigpio .INPUT )
59- self .pi .set_mode (PIN_ENCODER_LEFT , pigpio .INPUT )
60- self .pi .set_mode (PIN_ENCODER_RIGHT , pigpio .INPUT )
55+ # self.pi.set_mode(PIN_ENCODER_LEFT, pigpio.INPUT)
56+ # self.pi.set_mode(PIN_ENCODER_RIGHT, pigpio.INPUT)
6157 self ._cb = dict ()
6258 self ._cb_last_tick = dict ()
6359 self ._cb_elapse = dict ()
@@ -66,38 +62,40 @@ def __init__(self, servo=False, motor_trim_factor=1.0):
6662 if self ._servo :
6763 self .motor_control = self ._servo_motor
6864 else :
69- self .motor_control = self ._dc_motor
70- self ._cb1 = self .pi .callback (PIN_PUSHBUTTON , pigpio .EITHER_EDGE , coderbot_callback )
71- self ._cb2 = self .pi .callback (PIN_ENCODER_LEFT , pigpio .RISING_EDGE , coderbot_callback )
72- self ._cb3 = self .pi .callback (PIN_ENCODER_RIGHT , pigpio .RISING_EDGE , coderbot_callback )
65+ self .motor_control = self ._dc_enc_motor
66+ self ._cb1 = self .pi .callback (PIN_PUSHBUTTON , pigpio .EITHER_EDGE , self . _cb_button )
67+ # self._cb2 = self.pi.callback(PIN_ENCODER_LEFT, pigpio.RISING_EDGE, self._cb_enc_left )
68+ # self._cb3 = self.pi.callback(PIN_ENCODER_RIGHT, pigpio.RISING_EDGE, self._cb_enc_right )
7369 for pin in self ._pin_out :
7470 self .pi .set_PWM_frequency (pin , PWM_FREQUENCY )
7571 self .pi .set_PWM_range (pin , PWM_RANGE )
7672
77- self .stop ()
78- self ._is_moving = False
7973 self .sonar = [sonar .Sonar (self .pi , PIN_SONAR_1_TRIGGER , PIN_SONAR_1_ECHO ),
8074 sonar .Sonar (self .pi , PIN_SONAR_2_TRIGGER , PIN_SONAR_2_ECHO ),
8175 sonar .Sonar (self .pi , PIN_SONAR_3_TRIGGER , PIN_SONAR_3_ECHO )]
82- self ._encoder_cur_left = 0
83- self ._encoder_cur_right = 0
84- self ._encoder_target_left = - 1
85- self ._encoder_target_right = - 1
86- self ._encoder_k_s_1 = 20
87- self ._encoder_k_v_1 = 80
88- self ._encoder_sem = threading .Condition ()
89-
76+
77+ self ._twin_motors_enc = self .TwinMotorsEncoder (
78+ self .pi ,
79+ pin_enable = PIN_MOTOR_ENABLE ,
80+ pin_forward_left = PIN_LEFT_FORWARD ,
81+ pin_backward_left = PIN_LEFT_BACKWARD ,
82+ pin_encoder_left = PIN_ENCODER_LEFT ,
83+ pin_forward_right = PIN_RIGHT_FORWARD ,
84+ pin_backward_right = PIN_RIGHT_BACKWARD ,
85+ pin_encoder_right = PIN_ENCODER_RIGHT )
9086 try :
9187 self ._ag = mpu .AccelGyro ()
9288 except IOError :
9389 logging .info ("MPU not available" )
90+
91+ self .stop ()
92+ self ._is_moving = False
9493
9594 the_bot = None
9695
9796 def exit (self ):
9897 self ._cb1 .cancel ()
99- self ._cb2 .cancel ()
100- self ._cb3 .cancel ()
98+ self ._twin_motors_enc .exit ()
10199 for s in self .sonar :
102100 s .cancel ()
103101
@@ -148,7 +146,10 @@ def servo4(self, angle):
148146 def get_sonar_distance (self , sonar_id = 0 ):
149147 return self .sonar [sonar_id ].get_distance ()
150148
151- def _dc_motor (self , speed_left = 100 , speed_right = 100 , elapse = - 1 , steps_left = - 1 , steps_right = - 1 ):
149+ def _dc_enc_motor (self , speed_left = 100 , speed_right = 100 , elapse = - 1 , steps_left = - 1 , steps_right = - 1 ):
150+ self ._twin_motors_enc .control (power_left = speed_left , power_right = speed_right , elapse = elapse , speed_left = speed_left , speed_right = speed_right , steps_left = steps_left , steps_right = steps_right )
151+
152+ def _dc_motor (self , speed_left = 100 , speed_right = 100 , elapse = - 1 , steps_left = - 1 , steps_right = - 1 ):
152153 self ._encoder_cur_left = 0
153154 self ._encoder_cur_right = 0
154155 self ._encoder_target_left = steps_left
@@ -161,6 +162,8 @@ def _dc_motor(self, speed_left=100, speed_right=100, elapse=-1, steps_left=-1, s
161162 self ._encoder_motor_stopping_right = False
162163 self ._encoder_motor_stopped_left = False
163164 self ._encoder_motor_stopped_right = False
165+ self ._motor_current_power_left = speed_left
166+ self ._motor_current_power_right = speed_right
164167 if steps_left >= 0 or steps_right >= 0 :
165168 self ._encoder_sem .acquire ()
166169
@@ -221,8 +224,9 @@ def _servo_control(self, pin, angle):
221224 self .pi .set_PWM_dutycycle (pin , duty )
222225
223226 def stop (self ):
224- for pin in self ._pin_out :
225- self .pi .write (pin , 0 )
227+ self ._twin_motors_enc .stop ()
228+ #for pin in self._pin_out:
229+ # self.pi.write(pin, 0)
226230 self ._is_moving = False
227231
228232 def is_moving (self ):
@@ -233,73 +237,163 @@ def set_callback(self, gpio, callback, elapse):
233237 self ._cb [gpio ] = callback
234238 self ._cb_last_tick [gpio ] = 0
235239
236- def callback (self , gpio , level , tick ):
237- if gpio == PIN_ENCODER_LEFT and self ._encoder_target_left >= 0 :
238- self ._encoder_cur_left += 1
239- delta_ticks_left = tick - self ._encoder_last_tick_time_left if tick > self ._encoder_last_tick_time_left else tick - self ._encoder_last_tick_time_left + 4294967295
240- self ._encoder_last_tick_time_left = tick
241- self ._encoder_speed_left = 1000000.0 / delta_ticks_left #convert speed in steps per second
242- delta_s = self ._encoder_speed_left / self ._encoder_k_s_1 #delta_s is the delta (in steps)before the target to reverse the motor in order to arrive at target
243- if (self ._encoder_cur_left >= self ._encoder_target_left - delta_s and
244- not self ._encoder_motor_stopping_left ):
245- self ._encoder_motor_stopping_left = True
246- if self ._encoder_dir_left > 0 :
247- self .pi .write (PIN_LEFT_FORWARD , 0 )
248- self .pi .set_PWM_dutycycle (PIN_LEFT_BACKWARD , 100 )
249- else :
250- self .pi .set_PWM_dutycycle (PIN_LEFT_FORWARD , 100 )
251- self .pi .write (PIN_LEFT_BACKWARD , 0 )
252- elif (self ._encoder_motor_stopped_left == False and
253- ((self ._encoder_motor_stopping_left and
254- self ._encoder_speed_left < self ._encoder_k_v_1 ) or
255- (self ._encoder_motor_stopping_left and
256- self ._encoder_cur_left >= self ._encoder_target_left ))):
257- self .pi .write (PIN_LEFT_FORWARD , 0 )
258- self .pi .write (PIN_LEFT_BACKWARD , 0 )
259- self ._encoder_motor_stopped_left = True
260- self ._encoder_check_stopped_and_notify ()
261- logging .info ( "LEFT: " + str (self ._encoder_cur_left ) + " speed: " + str (self ._encoder_speed_left ))
262- elif gpio == PIN_ENCODER_RIGHT and self ._encoder_target_right >= 0 :
263- self ._encoder_cur_right += 1
264- delta_ticks_right = tick - self ._encoder_last_tick_time_right if tick > self ._encoder_last_tick_time_right else tick - self ._encoder_last_tick_time_right + 4294967295
265- self ._encoder_last_tick_time_right = tick
266- self ._encoder_speed_right = 1000000.0 / delta_ticks_right #convert speed in steps per second
267- delta_s = self ._encoder_speed_right / self ._encoder_k_s_1 #delta_s is the delta (in steps)before the target to reverse the motor in order to arrive at target
268- if (self ._encoder_cur_right >= self ._encoder_target_right - delta_s and
269- not self ._encoder_motor_stopping_right ):
270- self ._encoder_motor_stopping_right = True
271- if self ._encoder_dir_right > 0 :
272- self .pi .write (PIN_RIGHT_FORWARD , 0 )
273- self .pi .set_PWM_dutycycle (PIN_RIGHT_BACKWARD , 100 )
240+ def _cb_button (self , gpio , level , tick ):
241+ cb = self ._cb .get (gpio )
242+ if cb :
243+ elapse = self ._cb_elapse .get (gpio )
244+ if level == 0 :
245+ self ._cb_last_tick [gpio ] = tick
246+ elif tick - self ._cb_last_tick [gpio ] > elapse :
247+ self ._cb_last_tick [gpio ] = tick
248+ logging .info ( "pushed: " , level , tick )
249+ cb ()
250+
251+ class MotorEncoder (object ):
252+ def __init__ (self , parent , _pigpio , pin_enable , pin_forward , pin_backward , pin_encoder ):
253+ self ._parent = parent
254+ self ._pigpio = _pigpio
255+ self ._pin_enable = pin_enable
256+ self ._pin_forward = pin_forward
257+ self ._pin_backward = pin_backward
258+ self ._pin_encoder = pin_encoder
259+ self ._direction = False
260+ self ._pin_duty = 0
261+ self ._pin_reverse = 0
262+ self ._power = 0.0
263+ self ._encoder_dist = 0
264+ self ._encoder_speed = 0.0
265+ self ._encoder_last_tick = 0
266+ self ._encoder_dist_target = 0
267+ self ._encoder_speed_target = 0.0
268+ self ._encoder_k_s_1 = 20
269+ self ._encoder_k_v_1 = 80
270+ self ._motor_stopping = False
271+ self ._motor_running = False
272+ self ._pigpio .set_mode (self ._pin_encoder , pigpio .INPUT )
273+ self ._cb = self ._pigpio .callback (self ._pin_encoder , pigpio .RISING_EDGE , self ._cb_encoder )
274+
275+ def exit (self ):
276+ self ._cb .cancel ()
277+
278+ def _cb_encoder (self , gpio , level , tick ):
279+ self ._encoder_dist += 1
280+ delta_ticks = tick - self ._encoder_last_tick if tick > self ._encoder_last_tick else tick - self ._encoder_last_tick + 4294967295
281+ self ._encoder_last_tick = tick
282+ self ._encoder_speed = 1000000.0 / delta_ticks #convert speed in steps per second
283+ #print "speed: " + str(self._encoder_speed)
284+ if self ._encoder_dist_target >= 0 :
285+ delta_s = max (min (self ._encoder_speed / self ._encoder_k_s_1 , 70 ),0 ) #delta_s is the delta (in steps)before the target to reverse the motor in order to arrive at target
286+ #print "pin: " + str(self._pin_forward) + " dist: " + str(self._encoder_dist) + " target: " + str(self._encoder_dist_target) + " delta_s: " + str(delta_s)
287+ if (self ._encoder_dist >= self ._encoder_dist_target - delta_s and
288+ not self ._motor_stopping and self ._motor_running ):
289+ self ._motor_stopping = True
290+ self ._pigpio .write (self ._pin_duty , 0 )
291+ self ._pigpio .set_PWM_dutycycle (self ._pin_reverse , self ._power )
292+ #print "stopping"
293+ elif (self ._motor_running and
294+ ((self ._motor_stopping and
295+ self ._encoder_speed < self ._encoder_k_v_1 ) or
296+ (self ._motor_stopping and
297+ self ._encoder_dist >= self ._encoder_dist_target ))):
298+ self .stop ()
299+ self ._parent ._check_complete ()
300+ #print "stopped - pin: " + str(self._pin_forward) + " dist: " + str(self._encoder_dist) + " target: " + str(self._encoder_dist_target)
301+ logging .info ( "dist: " + str (self ._encoder_dist ) + " speed: " + str (self ._encoder_speed ))
274302 else :
275- self .pi .set_PWM_dutycycle (PIN_RIGHT_FORWARD , 100 )
276- self .pi .write (PIN_RIGHT_BACKWARD , 0 )
277- elif (self ._encoder_motor_stopped_right == False and
278- ((self ._encoder_motor_stopping_right and
279- self ._encoder_speed_right < self ._encoder_k_v_1 ) or
280- (self ._encoder_motor_stopping_right and
281- self ._encoder_cur_right >= self ._encoder_target_right ))):
282- self .pi .write (PIN_RIGHT_FORWARD , 0 )
283- self .pi .write (PIN_RIGHT_BACKWARD , 0 )
284- self ._encoder_motor_stopped_right = True
285- self ._encoder_check_stopped_and_notify ()
286- logging .info ("RIGHT: " + str (self ._encoder_cur_right ) + " speed: " + str (self ._encoder_speed_right ))
287- else :
288- cb = self ._cb .get (gpio )
289- if cb :
290- elapse = self ._cb_elapse .get (gpio )
291- if level == 0 :
292- self ._cb_last_tick [gpio ] = tick
293- elif tick - self ._cb_last_tick [gpio ] > elapse :
294- self ._cb_last_tick [gpio ] = tick
295- logging .info ( "pushed: " , level , tick )
296- cb ()
297-
298- def _encoder_check_stopped_and_notify (self ):
299- if self ._encoder_motor_stopped_left and self ._encoder_motor_stopped_right :
300- self ._encoder_sem .acquire ()
301- self ._encoder_sem .notify ()
302- self ._encoder_sem .release ()
303+ self ._parent ._cb_encoder (self , gpio , level , tick )
304+
305+ def control (self , power = 100.0 , elapse = - 1 , speed = 100.0 , steps = - 1 ):
306+ self ._direction = speed > 0
307+ self ._encoder_dist_target = steps
308+ self ._motor_stopping = False
309+ self ._motor_running = True
310+ self ._encoder_dist = 0
311+ self ._encoder_speed_target = abs (speed )
312+ self ._power = abs (power ) #TODO: initial power must be a function of desired speed
313+ self ._pin_duty = self ._pin_forward if self ._direction else self ._pin_backward
314+ self ._pin_reverse = self ._pin_backward if self ._direction else self ._pin_forward
315+ self ._pigpio .write (self ._pin_reverse , 0 )
316+ self ._pigpio .set_PWM_dutycycle (self ._pin_duty , self ._power )
317+ self ._pigpio .write (self ._pin_enable , True )
318+ if elapse > 0 :
319+ time .sleep (elapse )
320+ self .stop ()
321+
322+ def stop (self ):
323+ self ._motor_stopping = False
324+ self ._motor_running = False
325+ self ._pigpio .write (self ._pin_forward , 0 )
326+ self ._pigpio .write (self ._pin_backward , 0 )
327+
328+ def distance (self ):
329+ return self ._encoder_dist
330+
331+ def speed (self ):
332+ return self ._encoder_speed
333+
334+ def stopping (self ):
335+ return self ._motor_stopping
336+
337+ def running (self ):
338+ return self ._motor_running
339+
340+ def adjust_power (self , power_delta ):
341+ power = min (max (self ._power + power_delta , 0 ), 100 )
342+ self ._pigpio .set_PWM_dutycycle (self ._pin_duty , power )
343+
344+ class TwinMotorsEncoder (object ):
345+ def __init__ (self , pigpio , pin_enable , pin_forward_left , pin_backward_left , pin_encoder_left , pin_forward_right , pin_backward_right , pin_encoder_right ):
346+ self ._motor_left = CoderBot .MotorEncoder (self , pigpio , pin_enable , pin_forward_left , pin_backward_left , pin_encoder_left )
347+ self ._motor_right = CoderBot .MotorEncoder (self , pigpio , pin_enable , pin_forward_right , pin_backward_right , pin_encoder_right )
348+ self ._straight = False
349+ self ._running = False
350+ self ._encoder_sem = threading .Condition ()
351+
352+ def exit (self ):
353+ self ._motor_left .exit ()
354+ self ._motor_right .exit ()
355+
356+ def control (self , power_left = 100.0 , power_right = 100.0 , elapse = - 1 , speed_left = - 1 , speed_right = - 1 , steps_left = - 1 , steps_right = - 1 ):
357+ self ._straight = power_left == power_right and speed_left == speed_right and steps_left == steps_right
358+
359+ if steps_left >= 0 or steps_right >= 0 :
360+ self ._encoder_sem .acquire ()
361+
362+ self ._motor_left .control (power = power_left , elapse = - 1 , speed = speed_left , steps = steps_left )
363+ self ._motor_right .control (power = power_right , elapse = - 1 , speed = speed_right , steps = steps_right )
364+ self ._running = True
365+
366+ if elapse > 0 :
367+ time .sleep (elapse )
368+ self .stop ()
369+
370+ if steps_left >= 0 or steps_right >= 0 :
371+ self ._encoder_sem .wait ()
372+ self ._encoder_sem .release ()
373+ self .stop ()
374+
375+ def stop (self ):
376+ self ._motor_left .stop ()
377+ self ._motor_right .stop ()
378+ self ._running = False
379+
380+ def _cb_encoder (self , motor , gpio , level , tick ):
381+ if (self ._straight and self ._running and not self ._motor_left .stopping () and not self ._motor_right .stopping () and
382+ abs (self ._motor_left .distance () - self ._motor_right .distance ()) > 2 ):
383+ distance_delta = self ._motor_left .distance () - self ._motor_right .distance ()
384+ speed_delta = self ._motor_left .speed () - self ._motor_right .speed ()
385+ power_delta = (distance_delta / 2.0 ) + (speed_delta / 10.0 )
386+ #print "power_delta: " + str(power_delta) + " distance_delta: " + str(distance_delta) + " speed_delta: " + str(speed_delta)
387+ if self ._motor_left == motor :
388+ self ._motor_left .adjust_power (- power_delta )
389+ if self ._motor_right == motor :
390+ self ._motor_right .adjust_power (power_delta )
391+
392+ def _check_complete (self ):
393+ if self ._motor_left .running () == False and self ._motor_right .running () == False :
394+ self ._encoder_sem .acquire ()
395+ self ._encoder_sem .notify ()
396+ self ._encoder_sem .release ()
303397
304398 def halt (self ):
305399 os .system ('sudo halt' )
0 commit comments