Rug Warrior Pi: Build Blog (a PiDA bot)

Moderator: Steve

Re: Rug Warrior Pi: Build Blog (a PiDA bot)

Postby alanmcdonley » Sat Jun 18, 2016 7:32 am

Thanks,

I just updated the post with a link to the report (with Mikronauts.com Pi Droid Alpha mention) on element14.

Once the bot "has a brain", I want to investigate driving onto one of the Qi wireless charging pads.

Alan
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

Re: Rug Warrior Pi: Build Blog (a PiDA bot)

Postby mikronauts » Mon Jun 20, 2016 10:38 am

Looks good - excellent chart showing the "knee"

alanmcdonley wrote:Thanks,

I just updated the post with a link to the report (with Mikronauts.com Pi Droid Alpha mention) on element14.

Once the bot "has a brain", I want to investigate driving onto one of the Qi wireless charging pads.

Alan
mikronauts
 
Posts: 119
Joined: Tue Sep 16, 2014 6:58 pm

C&C? Whimpy Robot Design

Postby alanmcdonley » Fri Jun 24, 2016 10:05 am

Looking for C&C of my Whimpy Robot design:

https://goo.gl/xB4ybc

Sample Bumper Class with polling thread to read the bumpers asynchronously from the consumer (test main() in the same file) and my control-c handler that can call a class object's cancel function to kill the thread and clean up: (I have the feeling I don't need the global statements.)

Code: Select all
#
# myPyLib.py   SUPPLIMENTAL PYTHON FUNCTIONS
#
# v0.1   19June2016 

import time
import sys
import signal

# ######### CNTL-C #####
# Callback and setup to catch control-C and quit program

_funcToRun=None

def signal_handler(signal, frame):
  print '\n** Control-C Detected'
  if (_funcToRun != None):
     _funcToRun()
  sys.exit(0)

# Setup the callback to catch control-C
def set_cntl_c_handler(toRun):
  global _funcToRun
  _funcToRun = toRun
  signal.signal(signal.SIGINT, signal_handler)


Code: Select all
#!/usr/bin/python
#
# bumbersClass.py   BUMPERS SENSOR CLASS
#

import PDALib
import myPDALib
import myPyLib
import time
import sys
import threading


class Bumpers():

  # CLASS VARS (Avail to all instances)
  # Access as Bumpers.class_var_name

  pollThreadHandle=None   # the SINGLE read sensor thread for the Bumpers class   
  tSleep=0.033            # time for read_sensor thread to sleep after each read op
 
  # Bumpers are on the Pi Droid Alpha MCP23S17 DIO expander
  # Wired to use internal pull-up power of the MCP23S17
  # Bumper value is negative logic - 0 means bumper activated, normal 1

  LeftBumperDIO=18
  RightBumperDIO=17
  RearBumperDIO=16

  # Allowable Bumpers.state values
  NONE     = 0
  # Single bumpers
  LEFT     = 1   
  RIGHT    = 2
  REAR     = 4
  # Combinations
  FRONT    = 3
  LEFTREAR = 5
  RIGHTREAR= 6
  ALL      = 7
  # Not possible
  UNKNOWN  = 8
 
  # THE STATE OF EACH BUMPER and the combined BUMPERS state
  # (class vars because there are only one physical bumper)
  # note: can get rid of left(), right(), rear() methods by using these vars direct
  leftState= UNKNOWN
  rightState=UNKNOWN
  rearState= UNKNOWN
  state=     UNKNOWN  #0,1=L,2=R,3=L+R(front),4=Rear,...

  # use to print Bumper.state var
  bumperStrings=["NONE", "LEFT", "RIGHT", "FRONT", "REAR",
               "LEFTREAR", "RIGHTREAR", "ALL", "UNKNOWN"]
      
  # end of class vars definition

  def __init__(self):
    # SINGLETON TEST
    if (Bumpers.pollThreadHandle!=None):
        print "Second Bumpers Class Object, not starting pollingThread"
        return None
 
    # Set Bumper DIO channels as input for now
    PDALib.pinMode(Bumpers.LeftBumperDIO, PDALib.INPUT)
    PDALib.pinMode(Bumpers.RightBumperDIO,PDALib.INPUT)
    PDALib.pinMode(Bumpers.RearBumperDIO, PDALib.INPUT)

    # Set internal pull-ups on bumper channels
    PDALib.setDioBit( PDALib.DIO_GPPU, 8 )  # set LeftBumper  pin 16 pull-up
    PDALib.setDioBit( PDALib.DIO_GPPU, 9 )  # set RightBumper pin 17 pull-up
    PDALib.setDioBit( PDALib.DIO_GPPU, 10 ) # set RearBumper  pin 18 pull-up
    # threading target must be an instance
    Bumpers.pollThreadHandle = threading.Thread( target=self.pollBumpers,
                                               args=(Bumpers.tSleep,))
    Bumpers.pollThreadHandle.start()
  #end init()

  # BUMPER THREAD WORKER METHOD TO READ BUMPERS
  def pollBumpers(self,tSleep=0.01):     
    print "pollBumpers started with %f" % tSleep
    t = threading.currentThread()   # get handle to self (pollingBumpers thread)
    while getattr(t, "dorun", True):  # check the dorun thread attribute
      self.read()
      time.sleep(tSleep)
    print("dorun went false. Stopping pollBumpers thread")

   
  def read(self):  #READ THE BUMPERS - can be used as poll or directly
      Bumpers.leftState= Bumpers.LEFT - Bumpers.LEFT * \
                    PDALib.digitalRead(Bumpers.LeftBumperDIO)
      Bumpers.rightState= Bumpers.RIGHT - Bumpers.RIGHT * \
                      PDALib.digitalRead(Bumpers.RightBumperDIO)
      Bumpers.rearState=  Bumpers.REAR - Bumpers.REAR * \
                  PDALib.digitalRead(Bumpers.RearBumperDIO)
      Bumpers.state = Bumpers.leftState + Bumpers.rightState + Bumpers.rearState
      return Bumpers.state

  def status(self):
    return Bumpers.state

  def left(self):
    return Bumpers.leftState

  def right(self):
    return Bumpers.rightState

  def rear(self):
    return Bumpers.rearState
   
  def toString(self,bumperState=UNKNOWN): 
    if (bumperState==Bumpers.UNKNOWN):
       bumperState= Bumpers.state
    return Bumpers.bumperStrings[bumperState]

  def cancel(self):
     print "bumpers.cancel() called"
     self.pollThreadHandle.dorun = False
     myPDALib.PiExit()


# ##### BUMPER CLASS TEST METHOD ######
# creates two instances, only the first should start the read() thread
# the first time through the main() while loop, the sensors may not have been read yet
#     so bumpers.status() and each bumper may have a value of 8/UNKNOWN
def main():
  # note: lowercase bumpers is object, uppercase Bumpers is class (everywhere in code)
  bumpers=Bumpers()  #create an instance which starts the read bumpers thread
  bumpersNoThreadStart=Bumpers()  # Test a second instance of class
  myPyLib.set_cntl_c_handler(bumpers.cancel)  # Set CNTL-C handler
  while True:
      print "\n"
      print "bumpers.state: %d %s" % (bumpers.status(), bumpers.toString())
      print "left():%d  rear():%d  right():%d" % (
   bumpers.left(),
   bumpers.rear(),
   bumpers.right() )
      time.sleep(1)
  #end while
 

if __name__ == "__main__":
    main()
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

C&C? Wimpy Robot Code started

Postby alanmcdonley » Sat Jun 25, 2016 8:47 am

Here's a start on the wimpy robot code - notices when bumped, happy when not
(No one want's a depressed robot)

Code: Select all
#!/usr/bin/python
#
# whimpy.py   WHIMPY ROBOT
#

import PDALib
import myPDALib
import myPyLib
from bumpersClass import Bumpers
#from usDistanceClass import UltrasonicDistance
# import motors
import time


class Robot():
  # class constants and vars
  HAPPY=0
  BUMPED=1

  def __init__(self):
      print "Robot__init__"
      self.lastState=Robot.HAPPY       # create an instance var self.lastState
      self.bumpers=Bumpers()           # give robot instance bumpers
#     usDistance=UltrasonicDistance()  # give robot instance ultrasonic sensor
#     motors=Motors()

  def be_wimpy(self):
    while True:
      while (self.bumpers.status() == Bumpers.NONE):
        if (self.lastState!=Robot.HAPPY):
          print "\nI'm happy now"
          self.lastState=Robot.HAPPY
        continue
      # MUST HAVE BEEN BUMPED
      if (self.lastState!=Robot.BUMPED):
          print "\nI've been bumped! (%s)" % self.bumpers.toString()
          self.lastState=Robot.BUMPED
 
  def cancel(self):
     print "robot.cancel() called"
     self.bumpers.cancel()

#end Robot() class

# ##### MAIN ######
def main():
  try:
    print "Starting Main"
    r=Robot()
    time.sleep(1)  # allow all Robot threads to start
    myPyLib.set_cntl_c_handler(r.cancel)  # Set CNTL-C handler
    r.be_wimpy()
  except SystemExit:
    print "Bye Bye"   
  except:
    print "Exception Raised"
    r.cancel()
   

 
if __name__ == "__main__":
    main()

alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

Exception during PDALib spi.xfer "Bad file descriptor"

Postby alanmcdonley » Sun Jul 03, 2016 11:16 am

I was getting frequent exceptions along a common code path - PDALib.digitalWrite to set motor power level

NO PROBLEM FIRST FEW TIMES:
Code: Select all
pi@raspberrypi:~/RWPi $ ./whimpy.py
Starting Main
Robot__init__
readingsPerSec: 10
pollBumpers started with 0.100000
reading thread told to start
pollUltrasonicDistance started with 0.100000
Motors worker thread readingsPerSec: 20
Motors worker thread told to start 2016-07-03 13:57:04.322502
pollMotors thread started with 0.050000 at 2016-07-03 13:57:04.325529
waiting for threads to start

I'm happy now

I've been bumped! (REAR)
Checking if escape path is clear
Forward path is clear for: 23 inches
Moving fwd half my size
starting travel 3.5 at 1
waitForStopped or 60.0

I'm happy now

I've been bumped! (REAR)
Checking if escape path is clear
Forward path is clear for: 21 inches
Moving fwd half my size
starting travel 3.5 at 1
waitForStopped or 60.0

I'm happy now

I've been bumped! (REAR)
Checking if escape path is clear
Forward path is clear for: 17 inches
Moving fwd half my size
starting travel 3.5 at 1
waitForStopped or 60.0


THE EXCEPTION
File "/home/pi/RWPi/PDALib.py", line 324, in writeDio
r = spi.xfer([0x40,reg,val&255,val>>8])
IOError: [Errno 9] Bad file descriptor


OCCURRED THIS TIME:
Code: Select all
I'm happy now

I've been bumped! (REAR)
Checking if escape path is clear
Forward path is clear for: 14 inches
Moving fwd half my size
starting travel 3.5 at 1
waitForStopped or 60.0
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/pi/RWPi/motorsClass.py", line 258, in pollMotors
    self.control()
  File "/home/pi/RWPi/motorsClass.py", line 430, in control
    elif (self.motorsMode == Motors.TRAVEL): self.controlTravel()
  File "/home/pi/RWPi/motorsClass.py", line 342, in controlTravel
    self.setMotorsPwr(lPwr,rPwr)  # pwrs=(lPwr,rPwr)
  File "/home/pi/RWPi/motorsClass.py", line 585, in setMotorsPwr
    PDALib.digitalWrite(Motors.M2DirB,0)  #set to off/coast
  File "/home/pi/RWPi/PDALib.py", line 188, in digitalWrite
    return clearDioBit(DIO_OLAT,pin-8)
  File "/home/pi/RWPi/PDALib.py", line 353, in clearDioBit
    writeDio(reg,t)
  File "/home/pi/RWPi/PDALib.py", line 324, in writeDio
    r = spi.xfer([0x40,reg,val&255,val>>8])
IOError: [Errno 9] Bad file descriptor

^C
** Control-C Detected
robot.cancel() called
bumpers.cancel() called
Motors.cancel() called
Waiting for Motors.control Thread to quit
UltrasonicDistance.cancel() called
Waiting for UtrasonicDistance.readThread to quit
do_run went false. Stopping pollBumpers thread
do_run went false. Stopping pollUltrasonicDistance thread
myPDALib.PiExit():  PDALib.pi.stop() called
whimpy.py says: Bye Bye
pi@raspberrypi:~/RWPi $



By adding the lines to PDALib.v93.py:
Code: Select all
  import threading
  dio_lock=threading.Lock()


And this:
Code: Select all
  with dio_lock:  # critical section #ALAN


to:

pinMode()
digitalRead()
digitalWrite()
analogRead()
analogWrite()
servoWrite()

I have not had an exception this evening. Hopefully this is a good fix.
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

Re: Rug Warrior Pi: Build Blog (a PiDA bot)

Postby mikronauts » Mon Jul 04, 2016 11:38 am

As PiDroidAlpha was designed for small educational robots, I did not think that it would be called from multiple threads...

protecting readDio(), writeDio() and analogRead() with critical sections inside their definitions should make it thread safe (as you've deduced)

(Just got back from a 10 day vacation)
mikronauts
 
Posts: 119
Joined: Tue Sep 16, 2014 6:58 pm

The Life of Whimpy - First Rug Warrior Pi behavior

Postby alanmcdonley » Mon Jul 04, 2016 6:52 pm

My "Whimpy Robot" is in the test phase now. Things I learned through this exercise:
    Python Class structure
    Initializing class objects
    Thinking with "self"
    Threading (creating, managing, killing, critical sections, mutex variable)
    "Thinking asynchronous": (fire and forget, fire-go_away-then-resynchronize)
    Use of debugLevel to control print statements
    Maintaining state for objects and threads
    Publishing and consuming information for other objects
    Making decisions about self state and state of other objects
    Handling control-C to gracefully shutdown all objects and threads
    Designing and coding for reuse

So here is a sample run that shows:
Whimpy Robot startup

Whimpy is "Happy"

Whimpy is "Bumped" (from the REAR)
-decides escape path is straight forward
-checks coast is clear to move
-moves 4"

Whimpy is "Happy" again

Whimpy is "Bumped" (from the FRONT)
-decides escape path requires a 180 degree turn
-after the turn, decides coast is not clear
-decides to try an additional 45 degree turn
-checks this new escape path is clear
-moves 4"

Whimpy is "Happy" again

Control-C occurs - Whimpy shuts down gracefully

Code: Select all
pi@raspberrypi:~/RWPi $ ./whimpy.py
Starting Main
Robot__init__
UltrasonicDistance: readingsPerSec: 10
pollBumpers started with 0.100 interval
UltrasonicDistance: reading thread told to start
pollUltrasonicDistance started with 0.100s cycle
Motors: worker thread readingsPerSec: 20
Motors worker thread told to start 2016-07-04 09:40:04.233426
Motors: pollMotors thread started with 0.050000 at 2016-07-04 09:40:04.236642
waiting for threads to start

I'm happy now

********* RWPi STATUS *****
2016-07-04 09:40:06.258264
battery.volts(): 8.1
battery.hoursOfLifeRemaining(): 10 h 26 m
currentsensor.current_sense(): 524 mA
irDistance.inInches: 26.9
usDistance.inInches: 28.8
bumpers: NONE

I've been bumped! (REAR)
Checking if escape path is clear
Forward path is clear for: 8 inches
Moving fwd half my size
starting travel 3.5 at 1
waitForStopped or 60.0

I'm happy now

********* RWPi STATUS *****
2016-07-04 09:40:12.003888
battery.volts(): 8.1
battery.hoursOfLifeRemaining(): 10 h 25 m
currentsensor.current_sense(): 528 mA
irDistance.inInches: 28.1
usDistance.inInches: 26.9
bumpers: NONE

I've been bumped! (FRONT)

* Turning to CW180 as escape path
waitForStopped or 60.0
Checking if escape path is clear
Forward path is clear for: 2 inches

* Turning to CCW45 as escape path
waitForStopped or 60.0
Checking if escape path is clear
Forward path is clear for: 15 inches
Moving fwd half my size
starting travel 3.5 at 1
waitForStopped or 60.0

I'm happy now
********* RWPi STATUS *****
2016-07-04 09:41:13.900162
battery.volts(): 8.1
battery.hoursOfLifeRemaining(): 10 h 24 m
currentsensor.current_sense(): 508 mA
irDistance.inInches: 48.0
usDistance.inInches: 0.0
bumpers: NONE
^C
** Control-C Detected
robot.cancel() called
bumpers.cancel() called
Motors.cancel() called
Waiting for Motors.control Thread to quit
do_run went false. Stopping pollMotors thread at 2016-07-04 09:41:52.735483
UltrasonicDistance.cancel() called
Waiting for UtrasonicDistance.readThread to quit
do_run went false. Stopping pollUltrasonicDistance thread
do_run went false. Stopping pollBumpers thread
myPDALib.PiExit():  PDALib.pi.stop() called
whimpy.py says: Bye Bye
pi@raspberrypi:~/RWPi $
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

Rug Warrior Pi: The Photos

Postby alanmcdonley » Wed Jul 06, 2016 8:39 pm

I put up some photos with details about my Pi Droid Alpha / Raspberry Pi / Rug Warrior Pro robot:

Pigo - the Rug Warrior Pi robot photo album

Pigo.jpg
Pigo.jpg (27.08 KiB) Viewed 4295 times
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

Whinings of a tired robot builder

Postby alanmcdonley » Sun Jul 10, 2016 5:28 pm

I managed to get the MCP23S17 programmed to throw the portB interrupt pin for the encoder transitions,
(MCP23S17 Port B interrupt is wired to a Servo pin),

I got the GPIO callback set up.

encoders->MCP23S17_PortB->portB_Interrupt->GPIO->pigpiod->encoder_callback

Everyone is talking like a charm, well when the bot is going backward at least.

When the bot is going forward, the left encoder only registers 60% of the right encoder clicks.

Going backward (uncorrected drive), the "bias" is always less than 4% error in 48 inches, usually around 2%, and that might be motor variation, not lost clicks.

The bot designers (Joe Jones, Anita Flynn, Bruce Seiger) put the motor drive and the encoders in a ribbon cable, so it is very possible that this is a case of crosstalk.

Software is so easy to figure out what is going wrong. Hardware is such a mystery without a scope.

Anyway - I'm whining.
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

Obituary for a dead robot

Postby alanmcdonley » Wed Jul 13, 2016 8:38 pm

I must have fried something good on my Pi Droid Alpha board. It started with missing 40% of my left encoder pulses. After disassembly to recheck wiring and connections, I reassembled with the bumpers plugged in wrong - to the 5v and ground (pins 9/10) of IO2 instead of B0, B1 (pins 7/8).

When I applied power to the board, the bumper was direct shorting the 2 amp 5v supply to ground.

After correcting my wiring, port B stopped working completely, and one motor doesn't run.

I tried a new 23S17 chip but strangely, the bot shows the exact same symptoms - dead port B and one motor.

Strangely the 3008 ADC was working a little, as I still can read the unregulated battery voltage.

Part of me wants to abandon PiDA, but I have so much invested - creating the physical stack, in the wiring and the software, that I'm just feeling beat.

As part of the disassembly, reassembly I took the opportunity to swap in a Pi 3, which gives me access to the get_throttled status. From this I discovered that my Pololu 2A 5v step-up/down supply is sagging at boot, so the bot comes up showing 0x50000 which is under-voltage and throttling occurred during boot-up. I was so liking this tiny switching supply. Not sure if a big cap will fix it.

Bummer. I was feeling like I was just about ready to stop playing with hardware and start writing some personality and functionality into my bot.
alanmcdonley
 
Posts: 90
Joined: Thu Jul 23, 2015 10:50 am
Location: Boynton Beach, Florida

PreviousNext

Return to Pi Droid Alpha

Who is online

Users browsing this forum: No registered users and 1 guest

cron