Difference between revisions of "French FX-7 robot"
(→Shift-Register driving of robot) |
m |
||
(24 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{Project | {{Project | ||
− | |picture= | + | |picture=Robots.jpg |
|ProjectSkills=Electronics, Soldering, Programming, Creative thinking, | |ProjectSkills=Electronics, Soldering, Programming, Creative thinking, | ||
− | |ProjectStatus= | + | |ProjectStatus=Finished |
|ProjectNiche=Electronics | |ProjectNiche=Electronics | ||
|ProjectPurpose=World domination | |ProjectPurpose=World domination | ||
}} | }} | ||
"Contrôle système! Contrôle système!" | "Contrôle système! Contrôle système!" | ||
+ | |||
+ | IT MOVES! | ||
== Overview == | == Overview == | ||
== System == | == System == | ||
− | == Direct Drive control via RaspPI == | + | The robot has a total of 5 motors of which 4 are capable of being driven into either direction. These control the tank-tracks , the hip and the arm. The fifth motor spins a gear that winds up a spring and launches one of the three darts that are loaded in the chest-magazine. |
+ | |||
+ | For purposes of clarity, we've numbered the motors accordingly: | ||
+ | |||
+ | * Motor 1 = Left Track | ||
+ | * Motor 2 = Right Track | ||
+ | * Motor 3 = Hip | ||
+ | * Motor 4 = Arms | ||
+ | * Motor 5 = Dart-gun | ||
+ | |||
+ | The first four motors are driven using an full H-bridge arrangement using 4 'big' transistors and two small-signal transistors arranged in such a way that the whole H-bridge can be driven with just 2 lines from a microcontroller; assuring that the right 'opposite transistors' get activated when given a 'HIGH' signal. | ||
+ | |||
+ | The last motor is a bit of an enigma. The arrangement of the transistors is very unclear; but it should be trivial to drive it with a FET or a darlington. | ||
+ | |||
+ | Next to the motors, the system has a plethora of red LEDs in it's visor, as well as two 'headlights' on either side that switch on when given a command in it's original configuration. | ||
+ | |||
+ | There is also a speaker that is used for indicating which command's it's been given. Primarily for the purposes of reporting that it's closing or opening it's arms or commencing shooting. It also allows/allowed the operator to speak into the remote-control and have his/her voice be produced from this speaker. | ||
+ | |||
+ | == New plan with the Carambola2== | ||
+ | |||
+ | The motor-driver board now has a Carambola2 device directly mounted onto it. The board gets power via an LM7805-based power-supply which regulates the power coming from the LithiumPolymer battery and makes the system fully self-contained. | ||
+ | |||
+ | The nice thing about the Carambola2 is that it has onboard WIFI and allows easy remote control this way; as well as having all the GPIO niceties that you might wish for. | ||
+ | Sadly, the SPI-bus on the device is not easily usable by default without a bit of kernel-work. It has been decided that it'd be easier to just re-wire things to use the GPIO's individually; it has enough of them available. | ||
+ | |||
+ | |||
+ | === Controlling the device === | ||
+ | |||
+ | THe device has been configured to use Bonjour to announce itself as 'milobot.local' and be reachable on account 'root'. Password is known to the right people. | ||
+ | |||
+ | Driving the GPIO's is rather easy. It requires a few preparatory steps: | ||
+ | For example GPIO15 | ||
+ | |||
+ | * Export the pin using 'echo 15 > /sys/class/gpio/export' | ||
+ | * This will create/enable the directory '/sys/class/gpio/gpio15' | ||
+ | * Configure the pin direction (input/output) 'echo out > /sys/class/gpio/gpio15/direction' | ||
+ | * Configure the pin state to 'off' 'echo 0 > /sys/class/gpi0/gpio15/value' | ||
+ | |||
+ | * Setting the pin to 'on' state involves writing a '1' to the 'value' in the last step. | ||
+ | |||
+ | === Work to be done === | ||
+ | |||
+ | It'd be good to have a python program working (in preparation to a python class) that will allow control over each motor-driver individually (forward, backward, stop) as well as perhaps some 'macros' (drive forward, drive backward, rotate left, rotate right, turn left, turn right, reverse turn left, reverse turn right.... perhaps more, as well as the arms, hinge, etc. | ||
+ | |||
+ | Next to that we really should work on putting a fuse-holder on the board to protect against short-circuits. We've already had one accident result in a completely vaporized bit of wire.. | ||
+ | |||
+ | |||
+ | === Work done === | ||
+ | |||
+ | * All the GPIO's have been connected and tested to drive the H-Bridges correctly | ||
+ | * Status LED's have been soldered next to each output to be able to debug issues with motor, output/etc. | ||
+ | ** Green = Forward | ||
+ | ** Red = Backward | ||
+ | * A bash-programs have been produced to allow initializing the GPIO's as well as controlling each of the first 4 motor drivers using single keys. | ||
+ | * A rewrite has been made that cleans up some code ; requires testing, however. | ||
+ | * Layout improvements | ||
+ | |||
+ | === Work to be done === | ||
+ | |||
+ | * It'd be good to have a python program working (in preparation to a python class) that will allow control over each motor-driver individually (forward, backward, stop) as well as perhaps some 'macros' (drive forward, drive backward, rotate left, rotate right, turn left, turn right, reverse turn left, reverse turn right.... perhaps more, as well as the arms, hinge, etc. | ||
+ | ** THIS HAS ALREADY BEEN DONE WITH BASH; BUT PYTHON/ANYTHING_USEFUL WOULD BE BETTER* | ||
+ | * Note: we are lacking Python/perl/whatever on this thing. We need to find out how to install stuff on OpenWRT | ||
+ | * Add a fuse between the power-connector and the rest of the circuit. | ||
+ | * Pins 23,22,21,20,19,18,17,16,15,1 can be used as GPIO. Some (like 17 for example) may be on at startup, wich is not very useful. We need to find out how to change this. | ||
+ | |||
+ | === Programs ==== | ||
+ | ==== Original 'initialize.sh' ==== | ||
+ | <pre> | ||
+ | |||
+ | #!/bin/sh | ||
+ | |||
+ | for i in 15 16 18 19 20 21 22 23 | ||
+ | do | ||
+ | echo Initializing Pin $i | ||
+ | echo Export pin $i | ||
+ | echo $i > /sys/class/gpio/export | ||
+ | echo Set direction to output for gpio$i | ||
+ | echo out > /sys/class/gpio/gpio$i/direction | ||
+ | echo Clearing state to zero on gpio$i | ||
+ | echo 0 > /sys/class/gpio/gpio$i/value | ||
+ | done | ||
+ | </pre> | ||
+ | |||
+ | ==== original 'command.sh' ==== | ||
+ | <pre> | ||
+ | #!/bin/sh | ||
+ | |||
+ | # q, a, z = left | ||
+ | # w,s,x = right | ||
+ | |||
+ | |||
+ | |||
+ | while true | ||
+ | do | ||
+ | read -n1 KEY | ||
+ | if [ "$KEY" == 'q' ]; then | ||
+ | echo 'Left Forwards'; | ||
+ | echo 1 > /sys/class/gpio/gpio15/value; | ||
+ | echo 0 > /sys/class/gpio/gpio16/value; | ||
+ | elif [ "$KEY" == 'a' ]; then | ||
+ | echo 'Left Stop'; | ||
+ | echo 0 > /sys/class/gpio/gpio15/value; | ||
+ | echo 0 > /sys/class/gpio/gpio16/value; | ||
+ | elif [ "$KEY" == 'z' ]; then | ||
+ | echo 'Left Backwards'; | ||
+ | echo 0 > /sys/class/gpio/gpio15/value; | ||
+ | echo 1 > /sys/class/gpio/gpio16/value; | ||
+ | elif [ "$KEY" == 'w' ]; then | ||
+ | echo 'Right Forwards'; | ||
+ | echo 1 > /sys/class/gpio/gpio18/value; | ||
+ | echo 0 > /sys/class/gpio/gpio19/value; | ||
+ | elif [ "$KEY" == 's' ]; then | ||
+ | echo 'Right Stop'; | ||
+ | echo 0 > /sys/class/gpio/gpio18/value; | ||
+ | echo 0 > /sys/class/gpio/gpio19/value; | ||
+ | elif [ "$KEY" == 'x' ]; then | ||
+ | echo 'Right Backwards'; | ||
+ | echo 0 > /sys/class/gpio/gpio18/value; | ||
+ | echo 1 > /sys/class/gpio/gpio19/value; | ||
+ | |||
+ | elif [ "$KEY" == 'e' ]; then | ||
+ | echo 'Bend Forwards'; | ||
+ | echo 1 > /sys/class/gpio/gpio20/value; | ||
+ | echo 0 > /sys/class/gpio/gpio21/value; | ||
+ | elif [ "$KEY" == 'd' ]; then | ||
+ | echo 'Bend Stop'; | ||
+ | echo 0 > /sys/class/gpio/gpio20/value; | ||
+ | echo 0 > /sys/class/gpio/gpio21/value; | ||
+ | elif [ "$KEY" == 'c' ]; then | ||
+ | echo 'Bend Backwards'; | ||
+ | echo 0 > /sys/class/gpio/gpio20/value; | ||
+ | echo 1 > /sys/class/gpio/gpio21/value; | ||
+ | |||
+ | elif [ "$KEY" == 'r' ]; then | ||
+ | echo 'Arms Close'; | ||
+ | echo 1 > /sys/class/gpio/gpio22/value; | ||
+ | echo 0 > /sys/class/gpio/gpio23/value; | ||
+ | elif [ "$KEY" == 'f' ]; then | ||
+ | echo 'Arms Stop'; | ||
+ | echo 0 > /sys/class/gpio/gpio22/value; | ||
+ | echo 0 > /sys/class/gpio/gpio23/value; | ||
+ | elif [ "$KEY" == 'v' ]; then | ||
+ | echo 'Arms Open'; | ||
+ | echo 0 > /sys/class/gpio/gpio22/value; | ||
+ | echo 1 > /sys/class/gpio/gpio23/value; | ||
+ | |||
+ | elif [ "$KEY" == '' ]; then | ||
+ | echo 'STOP'; | ||
+ | for i in 15 16 18 19 20 21 22 23 | ||
+ | do | ||
+ | echo 0 > /sys/class/gpio/gpio$i/value; | ||
+ | done | ||
+ | elif [ "$KEY" == 'p' ]; then | ||
+ | echo 'Quitting and resetting all outputs. Bye!'; | ||
+ | for i in 15 16 18 19 20 21 22 23 | ||
+ | do | ||
+ | echo 0 > /sys/class/gpio/gpio$i/value; | ||
+ | done | ||
+ | exit | ||
+ | else | ||
+ | echo "Other key: $KEY"; | ||
+ | fi | ||
+ | done | ||
+ | </pre> | ||
+ | |||
+ | ==== 'command.sh' 2.0 ==== | ||
+ | This one contains help, initialization in one go. Cleaned up a bit using a function for GPIO toggling and has some help-text. | ||
+ | |||
+ | To be done: | ||
+ | * suppress local echo of pressed key | ||
+ | * write action on fixed spot | ||
+ | * Provide status-map of motors above the bottom line; update it on change. | ||
+ | |||
+ | For now, it first needs testing.. it seems like it should work. | ||
+ | |||
+ | |||
+ | <pre> | ||
+ | #!/bin/sh | ||
+ | |||
+ | # q,a,z = left track (forwards/stop/backwards) | ||
+ | # w,s,x = right track (forwards/stop/backwards) | ||
+ | # e,d,c = hip hinge (forwards/stop/backwards) | ||
+ | # r,f,v = control arms (close/stop/open) | ||
+ | |||
+ | |||
+ | base="/sys/class/gpio" | ||
+ | ports='15 16 18 19 20 21 22 23' | ||
+ | |||
+ | function gpio(){ | ||
+ | # Sets gpio-pin $1 to state $2 | ||
+ | echo $2 > "$base/gpio$1/value" | ||
+ | } | ||
+ | |||
+ | echo 'Checking port initialization'; | ||
+ | for i in $ports | ||
+ | do | ||
+ | if [ ! -d "$base$i" ]; then | ||
+ | echo 'GPIO$i not initialized; exporting and configuring as output' | ||
+ | echo $i > "$base/export" | ||
+ | echo out > "$base/gpio$i/direction" | ||
+ | gpio $i 0 | ||
+ | fi | ||
+ | done | ||
+ | echo "Initialization done" | ||
+ | echo "Usage: forwards, stop, backwards" | ||
+ | echo "Left track : q, a, z" | ||
+ | echo "Right track: w, s, x" | ||
+ | echo "Hip hinge : e, d, c" | ||
+ | echo "Arms : r, f, v" | ||
+ | echo "Special keys:" | ||
+ | echo "Spacebar = Emergency stop" | ||
+ | echo "p = stop all motors and quit application" | ||
+ | |||
+ | while true | ||
+ | do | ||
+ | read -n1 KEY | ||
+ | if [ "$KEY" == 'q' ]; then | ||
+ | echo 'Left Forwards'; | ||
+ | gpio 15 1 | ||
+ | gpio 16 0 | ||
+ | elif [ "$KEY" == 'a' ]; then | ||
+ | echo 'Left Stop'; | ||
+ | gpio 15 0 | ||
+ | gpio 16 0 | ||
+ | elif [ "$KEY" == 'z' ]; then | ||
+ | echo 'Left Backwards'; | ||
+ | gpio 15 0 | ||
+ | gpio 15 1 | ||
+ | elif [ "$KEY" == 'w' ]; then | ||
+ | echo 'Right Forwards'; | ||
+ | gpio 18 1 | ||
+ | gpio 19 0 | ||
+ | elif [ "$KEY" == 's' ]; then | ||
+ | echo 'Right Stop'; | ||
+ | gpio 18 0 | ||
+ | gpio 19 0 | ||
+ | elif [ "$KEY" == 'x' ]; then | ||
+ | echo 'Right Backwards'; | ||
+ | gpio 18 0 | ||
+ | gpio 19 1 | ||
+ | elif [ "$KEY" == 'e' ]; then | ||
+ | echo 'Bend Forwards'; | ||
+ | gpio 20 1 | ||
+ | gpio 21 0 | ||
+ | elif [ "$KEY" == 'd' ]; then | ||
+ | echo 'Bend Stop'; | ||
+ | gpio 20 0 | ||
+ | gpio 21 0 | ||
+ | elif [ "$KEY" == 'c' ]; then | ||
+ | echo 'Bend Backwards'; | ||
+ | gpio 20 0 | ||
+ | gpio 21 1 | ||
+ | elif [ "$KEY" == 'r' ]; then | ||
+ | echo 'Arms Close'; | ||
+ | gpio 22 1 | ||
+ | gpio 23 0 | ||
+ | elif [ "$KEY" == 'f' ]; then | ||
+ | echo 'Arms Stop'; | ||
+ | gpio 22 0 | ||
+ | gpio 23 0 | ||
+ | elif [ "$KEY" == 'v' ]; then | ||
+ | echo 'Arms Open'; | ||
+ | gpio 22 0 | ||
+ | gpio 23 1 | ||
+ | elif [ "$KEY" == '' ]; then | ||
+ | echo 'STOP'; | ||
+ | for i in $ports | ||
+ | do | ||
+ | gpio $i 0 | ||
+ | done | ||
+ | elif [ "$KEY" == 'p' ]; then | ||
+ | echo 'Quitting and resetting all outputs. Bye!'; | ||
+ | for i in $ports | ||
+ | do | ||
+ | gpio $i 0 | ||
+ | done | ||
+ | exit | ||
+ | else | ||
+ | echo "Other key: $KEY"; | ||
+ | fi | ||
+ | done | ||
+ | </pre> | ||
+ | |||
+ | ==== Newest 'command.sh' ==== | ||
+ | <pre> | ||
+ | #!/bin/sh | ||
+ | |||
+ | # q,a,z = left track (forwards/stop/backwards) | ||
+ | # w,s,x = right track (forwards/stop/backwards) | ||
+ | # e,d,c = hip hinge (forwards/stop/backwards) | ||
+ | # r,f,v = control arms (close/stop/open) | ||
+ | # t,g = shoot (start/stop) | ||
+ | |||
+ | base="/sys/class/gpio" | ||
+ | ports='1 15 16 18 19 20 21 22 23' | ||
+ | |||
+ | gpio(){ | ||
+ | # Sets gpio-pin $1 to state $2 | ||
+ | echo $2 > "$base/gpio$1/value" | ||
+ | } | ||
+ | |||
+ | echo 'Checking port initialization'; | ||
+ | for i in $ports | ||
+ | do | ||
+ | if [ ! -d "$base$i" ]; then | ||
+ | echo "GPIO$i not initialized; exporting and configuring as output" | ||
+ | echo $i > "$base/export" | ||
+ | echo out > "$base/gpio$i/direction" | ||
+ | gpio $i 0 | ||
+ | fi | ||
+ | done | ||
+ | echo "Initialization done" | ||
+ | echo "Usage: forwards, stop, backwards" | ||
+ | echo "Left track : q, a, z" | ||
+ | echo "Right track: w, s, x" | ||
+ | echo "Hip hinge : e, d, c" | ||
+ | echo "Arms : r, f, v" | ||
+ | echo "Gun : t, g" | ||
+ | echo "Special keys:" | ||
+ | echo "Spacebar = Emergency stop" | ||
+ | echo "p = stop all motors and quit application" | ||
+ | |||
+ | while true | ||
+ | do | ||
+ | read -n1 KEY | ||
+ | if [ "$KEY" == 'q' ]; then | ||
+ | echo 'Left Forwards'; | ||
+ | gpio 15 1 | ||
+ | gpio 16 0 | ||
+ | elif [ "$KEY" == 'a' ]; then | ||
+ | echo 'Left Stop'; | ||
+ | gpio 15 0 | ||
+ | gpio 16 0 | ||
+ | elif [ "$KEY" == 'z' ]; then | ||
+ | echo 'Left Backwards'; | ||
+ | gpio 15 0 | ||
+ | gpio 16 1 | ||
+ | elif [ "$KEY" == 'w' ]; then | ||
+ | echo 'Right Forwards'; | ||
+ | gpio 18 1 | ||
+ | gpio 19 0 | ||
+ | elif [ "$KEY" == 's' ]; then | ||
+ | echo 'Right Stop'; | ||
+ | gpio 18 0 | ||
+ | gpio 19 0 | ||
+ | elif [ "$KEY" == 'x' ]; then | ||
+ | echo 'Right Backwards'; | ||
+ | gpio 18 0 | ||
+ | gpio 19 1 | ||
+ | elif [ "$KEY" == 'e' ]; then | ||
+ | echo 'Bend Forwards'; | ||
+ | gpio 20 1 | ||
+ | gpio 21 0 | ||
+ | elif [ "$KEY" == 'd' ]; then | ||
+ | echo 'Bend Stop'; | ||
+ | gpio 20 0 | ||
+ | gpio 21 0 | ||
+ | elif [ "$KEY" == 'c' ]; then | ||
+ | echo 'Bend Backwards'; | ||
+ | gpio 20 0 | ||
+ | gpio 21 1 | ||
+ | elif [ "$KEY" == 'r' ]; then | ||
+ | echo 'Arms Close'; | ||
+ | gpio 22 1 | ||
+ | gpio 23 0 | ||
+ | elif [ "$KEY" == 'f' ]; then | ||
+ | echo 'Arms Stop'; | ||
+ | gpio 22 0 | ||
+ | gpio 23 0 | ||
+ | elif [ "$KEY" == 'v' ]; then | ||
+ | echo 'Arms Open'; | ||
+ | gpio 22 0 | ||
+ | gpio 23 1 | ||
+ | |||
+ | elif [ "$KEY" == 't' ]; then | ||
+ | echo 'Gun Shoot'; | ||
+ | gpio 1 1 | ||
+ | elif [ "$KEY" == 'g' ]; then | ||
+ | echo 'Gun Stop'; | ||
+ | gpio 1 0 | ||
+ | |||
+ | elif [ "$KEY" == '' ]; then | ||
+ | echo 'STOP'; | ||
+ | for i in $ports | ||
+ | do | ||
+ | gpio $i 0 | ||
+ | done | ||
+ | elif [ "$KEY" == 'p' ]; then | ||
+ | echo 'Quitting and resetting all outputs. Bye!'; | ||
+ | for i in $ports | ||
+ | do | ||
+ | gpio $i 0 | ||
+ | done | ||
+ | exit | ||
+ | else | ||
+ | echo "Other key: $KEY"; | ||
+ | fi | ||
+ | done | ||
+ | </pre> | ||
+ | |||
+ | == Abandoned ideas == | ||
+ | === Direct Drive control via RaspPI === | ||
Machine has been taken apart.. blblblalbla | Machine has been taken apart.. blblblalbla | ||
Line 118: | Line 521: | ||
The proto-board has been re-wired to have two shift-registers hosted on it, connected in series, for a total of 16 outputs. | The proto-board has been re-wired to have two shift-registers hosted on it, connected in series, for a total of 16 outputs. | ||
+ | |||
+ | The shift-registers in question are the 74HCT595's that has an 8-bit serial register controlled by 'data' and 'clock' lines, and an 8-bit parallel 'latch' that is controlled by a separate line; allowing you to control at which moment the 8 output pins (output A-H) should reflect the state of the internal 8-bit serial register. | ||
+ | |||
+ | By having the parallel register in between the outputs and the serially controlled register, you are able to change the contents of the serial register at your own leisure without the outputs directly following your changes. | ||
+ | |||
+ | The last control-lines on the 74HCT595 are a 'master reset' pin (active LOW; so connect to HIGH if unused) and a 'Chip enable' (active LOW, connect to GND if not used) | ||
+ | |||
The motor-pins are connected to the first of the two shift-registers; this means that the last 8 bits of data shifted into the 16 bit register-space controls the motors as the first bit shifted in ends up being at the last-most position after 16 clock-cycles. | The motor-pins are connected to the first of the two shift-registers; this means that the last 8 bits of data shifted into the 16 bit register-space controls the motors as the first bit shifted in ends up being at the last-most position after 16 clock-cycles. | ||
− | |||
− | |||
{| class="wikitable" | {| class="wikitable" | ||
+ | !colspan="6"|Shift-register layout | ||
+ | |- | ||
! align="left"| Register | ! align="left"| Register | ||
! align="left"| Bit | ! align="left"| Bit | ||
Line 196: | Line 606: | ||
|TBD | |TBD | ||
|} | |} | ||
+ | |||
+ | === Software Side === | ||
+ | |||
+ | To drive the shift-register, two approaches can be used. Either full 'software bit-banging' or using the SPI-bus on the Raspberry PI to drive (at least) the clock, enable and data-lines of the shift-register. The 'latch' pin of the register needs to be bit-banged as there seems to be no easy way to pull this off with just SPI (unless.. perhaps, doing something smart with the 'enable' line..) | ||
+ | |||
+ | |||
+ | The pure bit-bang approach could use any 3 or 4 GPIO pins on the PI, using the work done on https://projects.drogon.net/raspberry-pi/gpio-examples/ as already used and listed above. | ||
+ | |||
+ | The SPI approach could use the work done on http://www.100randomtasks.com/simple-spi-on-raspberry-pi. | ||
+ | |||
+ | An example snippet of code could be | ||
+ | |||
+ | <pre> | ||
+ | import RPi.GPIO as gpio | ||
+ | import time | ||
+ | impport spidev | ||
+ | |||
+ | gpio.setmode(GPIO.BOARD) | ||
+ | gpio.setup(12,GPIO.OUT) | ||
+ | gpio.output(12,False) | ||
+ | |||
+ | spi = spidev.SpiDev() | ||
+ | spi.open(0,0) | ||
+ | while True: | ||
+ | resp = spi.xfer2([0x5555]) | ||
+ | print resp([0]) | ||
+ | gpio.output(12,True) | ||
+ | gpio.output(12,False) | ||
+ | time.sleep(1) | ||
+ | resp = spi.xfer2([0xAAAA]) | ||
+ | print resp([0]) | ||
+ | gpio.output(12,True) | ||
+ | gpio.output(12,False) | ||
+ | time.sleep(1) | ||
+ | </pre> | ||
+ | |||
+ | This will import the right libs, setup pin 12 as the 'latch' pin, initialize the SPI-bus using CE0 as the chip-enable line (open(0,0)), and then write a 0x5555 value to the SPI-bus; two bytes, aka 16 bits. 0x5555 and 0xAAAA are eachothers complements; an alternating pattern of 0 and 1's, starting either with a 0 (0x5555) or a 1 (0xAAAA). The 'resp' variable will be loaded with whatever comes back in on the input at the same time; if you connect MOSI to MISO , you will normally read the same thing you are writing out. You can also connect the H' pin of register2 to MISO; this will allow you to read the PREVIOUS state of the shift-registers back... for no useful reason, however. | ||
+ | |||
+ | Toggling pin 12 up and down should trigger the parallel latch to take the contents of the serial latch and adjust the outputs and then make it safe to adjust the serial register again for the next update. |
Latest revision as of 09:47, 1 October 2015
Projects | |
---|---|
Participants | Mi1es |
Skills | Electronics, Soldering, Programming, Creative thinking |
Status | Finished |
Niche | Electronics |
Purpose | World domination |
"Contrôle système! Contrôle système!"
IT MOVES!
Contents
Overview
System
The robot has a total of 5 motors of which 4 are capable of being driven into either direction. These control the tank-tracks , the hip and the arm. The fifth motor spins a gear that winds up a spring and launches one of the three darts that are loaded in the chest-magazine.
For purposes of clarity, we've numbered the motors accordingly:
- Motor 1 = Left Track
- Motor 2 = Right Track
- Motor 3 = Hip
- Motor 4 = Arms
- Motor 5 = Dart-gun
The first four motors are driven using an full H-bridge arrangement using 4 'big' transistors and two small-signal transistors arranged in such a way that the whole H-bridge can be driven with just 2 lines from a microcontroller; assuring that the right 'opposite transistors' get activated when given a 'HIGH' signal.
The last motor is a bit of an enigma. The arrangement of the transistors is very unclear; but it should be trivial to drive it with a FET or a darlington.
Next to the motors, the system has a plethora of red LEDs in it's visor, as well as two 'headlights' on either side that switch on when given a command in it's original configuration.
There is also a speaker that is used for indicating which command's it's been given. Primarily for the purposes of reporting that it's closing or opening it's arms or commencing shooting. It also allows/allowed the operator to speak into the remote-control and have his/her voice be produced from this speaker.
New plan with the Carambola2
The motor-driver board now has a Carambola2 device directly mounted onto it. The board gets power via an LM7805-based power-supply which regulates the power coming from the LithiumPolymer battery and makes the system fully self-contained.
The nice thing about the Carambola2 is that it has onboard WIFI and allows easy remote control this way; as well as having all the GPIO niceties that you might wish for. Sadly, the SPI-bus on the device is not easily usable by default without a bit of kernel-work. It has been decided that it'd be easier to just re-wire things to use the GPIO's individually; it has enough of them available.
Controlling the device
THe device has been configured to use Bonjour to announce itself as 'milobot.local' and be reachable on account 'root'. Password is known to the right people.
Driving the GPIO's is rather easy. It requires a few preparatory steps: For example GPIO15
- Export the pin using 'echo 15 > /sys/class/gpio/export'
- This will create/enable the directory '/sys/class/gpio/gpio15'
- Configure the pin direction (input/output) 'echo out > /sys/class/gpio/gpio15/direction'
- Configure the pin state to 'off' 'echo 0 > /sys/class/gpi0/gpio15/value'
- Setting the pin to 'on' state involves writing a '1' to the 'value' in the last step.
Work to be done
It'd be good to have a python program working (in preparation to a python class) that will allow control over each motor-driver individually (forward, backward, stop) as well as perhaps some 'macros' (drive forward, drive backward, rotate left, rotate right, turn left, turn right, reverse turn left, reverse turn right.... perhaps more, as well as the arms, hinge, etc.
Next to that we really should work on putting a fuse-holder on the board to protect against short-circuits. We've already had one accident result in a completely vaporized bit of wire..
Work done
- All the GPIO's have been connected and tested to drive the H-Bridges correctly
- Status LED's have been soldered next to each output to be able to debug issues with motor, output/etc.
- Green = Forward
- Red = Backward
- A bash-programs have been produced to allow initializing the GPIO's as well as controlling each of the first 4 motor drivers using single keys.
- A rewrite has been made that cleans up some code ; requires testing, however.
- Layout improvements
Work to be done
- It'd be good to have a python program working (in preparation to a python class) that will allow control over each motor-driver individually (forward, backward, stop) as well as perhaps some 'macros' (drive forward, drive backward, rotate left, rotate right, turn left, turn right, reverse turn left, reverse turn right.... perhaps more, as well as the arms, hinge, etc.
- THIS HAS ALREADY BEEN DONE WITH BASH; BUT PYTHON/ANYTHING_USEFUL WOULD BE BETTER*
- Note: we are lacking Python/perl/whatever on this thing. We need to find out how to install stuff on OpenWRT
- Add a fuse between the power-connector and the rest of the circuit.
- Pins 23,22,21,20,19,18,17,16,15,1 can be used as GPIO. Some (like 17 for example) may be on at startup, wich is not very useful. We need to find out how to change this.
Programs =
Original 'initialize.sh'
#!/bin/sh for i in 15 16 18 19 20 21 22 23 do echo Initializing Pin $i echo Export pin $i echo $i > /sys/class/gpio/export echo Set direction to output for gpio$i echo out > /sys/class/gpio/gpio$i/direction echo Clearing state to zero on gpio$i echo 0 > /sys/class/gpio/gpio$i/value done
original 'command.sh'
#!/bin/sh # q, a, z = left # w,s,x = right while true do read -n1 KEY if [ "$KEY" == 'q' ]; then echo 'Left Forwards'; echo 1 > /sys/class/gpio/gpio15/value; echo 0 > /sys/class/gpio/gpio16/value; elif [ "$KEY" == 'a' ]; then echo 'Left Stop'; echo 0 > /sys/class/gpio/gpio15/value; echo 0 > /sys/class/gpio/gpio16/value; elif [ "$KEY" == 'z' ]; then echo 'Left Backwards'; echo 0 > /sys/class/gpio/gpio15/value; echo 1 > /sys/class/gpio/gpio16/value; elif [ "$KEY" == 'w' ]; then echo 'Right Forwards'; echo 1 > /sys/class/gpio/gpio18/value; echo 0 > /sys/class/gpio/gpio19/value; elif [ "$KEY" == 's' ]; then echo 'Right Stop'; echo 0 > /sys/class/gpio/gpio18/value; echo 0 > /sys/class/gpio/gpio19/value; elif [ "$KEY" == 'x' ]; then echo 'Right Backwards'; echo 0 > /sys/class/gpio/gpio18/value; echo 1 > /sys/class/gpio/gpio19/value; elif [ "$KEY" == 'e' ]; then echo 'Bend Forwards'; echo 1 > /sys/class/gpio/gpio20/value; echo 0 > /sys/class/gpio/gpio21/value; elif [ "$KEY" == 'd' ]; then echo 'Bend Stop'; echo 0 > /sys/class/gpio/gpio20/value; echo 0 > /sys/class/gpio/gpio21/value; elif [ "$KEY" == 'c' ]; then echo 'Bend Backwards'; echo 0 > /sys/class/gpio/gpio20/value; echo 1 > /sys/class/gpio/gpio21/value; elif [ "$KEY" == 'r' ]; then echo 'Arms Close'; echo 1 > /sys/class/gpio/gpio22/value; echo 0 > /sys/class/gpio/gpio23/value; elif [ "$KEY" == 'f' ]; then echo 'Arms Stop'; echo 0 > /sys/class/gpio/gpio22/value; echo 0 > /sys/class/gpio/gpio23/value; elif [ "$KEY" == 'v' ]; then echo 'Arms Open'; echo 0 > /sys/class/gpio/gpio22/value; echo 1 > /sys/class/gpio/gpio23/value; elif [ "$KEY" == '' ]; then echo 'STOP'; for i in 15 16 18 19 20 21 22 23 do echo 0 > /sys/class/gpio/gpio$i/value; done elif [ "$KEY" == 'p' ]; then echo 'Quitting and resetting all outputs. Bye!'; for i in 15 16 18 19 20 21 22 23 do echo 0 > /sys/class/gpio/gpio$i/value; done exit else echo "Other key: $KEY"; fi done
'command.sh' 2.0
This one contains help, initialization in one go. Cleaned up a bit using a function for GPIO toggling and has some help-text.
To be done:
- suppress local echo of pressed key
- write action on fixed spot
- Provide status-map of motors above the bottom line; update it on change.
For now, it first needs testing.. it seems like it should work.
#!/bin/sh # q,a,z = left track (forwards/stop/backwards) # w,s,x = right track (forwards/stop/backwards) # e,d,c = hip hinge (forwards/stop/backwards) # r,f,v = control arms (close/stop/open) base="/sys/class/gpio" ports='15 16 18 19 20 21 22 23' function gpio(){ # Sets gpio-pin $1 to state $2 echo $2 > "$base/gpio$1/value" } echo 'Checking port initialization'; for i in $ports do if [ ! -d "$base$i" ]; then echo 'GPIO$i not initialized; exporting and configuring as output' echo $i > "$base/export" echo out > "$base/gpio$i/direction" gpio $i 0 fi done echo "Initialization done" echo "Usage: forwards, stop, backwards" echo "Left track : q, a, z" echo "Right track: w, s, x" echo "Hip hinge : e, d, c" echo "Arms : r, f, v" echo "Special keys:" echo "Spacebar = Emergency stop" echo "p = stop all motors and quit application" while true do read -n1 KEY if [ "$KEY" == 'q' ]; then echo 'Left Forwards'; gpio 15 1 gpio 16 0 elif [ "$KEY" == 'a' ]; then echo 'Left Stop'; gpio 15 0 gpio 16 0 elif [ "$KEY" == 'z' ]; then echo 'Left Backwards'; gpio 15 0 gpio 15 1 elif [ "$KEY" == 'w' ]; then echo 'Right Forwards'; gpio 18 1 gpio 19 0 elif [ "$KEY" == 's' ]; then echo 'Right Stop'; gpio 18 0 gpio 19 0 elif [ "$KEY" == 'x' ]; then echo 'Right Backwards'; gpio 18 0 gpio 19 1 elif [ "$KEY" == 'e' ]; then echo 'Bend Forwards'; gpio 20 1 gpio 21 0 elif [ "$KEY" == 'd' ]; then echo 'Bend Stop'; gpio 20 0 gpio 21 0 elif [ "$KEY" == 'c' ]; then echo 'Bend Backwards'; gpio 20 0 gpio 21 1 elif [ "$KEY" == 'r' ]; then echo 'Arms Close'; gpio 22 1 gpio 23 0 elif [ "$KEY" == 'f' ]; then echo 'Arms Stop'; gpio 22 0 gpio 23 0 elif [ "$KEY" == 'v' ]; then echo 'Arms Open'; gpio 22 0 gpio 23 1 elif [ "$KEY" == '' ]; then echo 'STOP'; for i in $ports do gpio $i 0 done elif [ "$KEY" == 'p' ]; then echo 'Quitting and resetting all outputs. Bye!'; for i in $ports do gpio $i 0 done exit else echo "Other key: $KEY"; fi done
Newest 'command.sh'
#!/bin/sh # q,a,z = left track (forwards/stop/backwards) # w,s,x = right track (forwards/stop/backwards) # e,d,c = hip hinge (forwards/stop/backwards) # r,f,v = control arms (close/stop/open) # t,g = shoot (start/stop) base="/sys/class/gpio" ports='1 15 16 18 19 20 21 22 23' gpio(){ # Sets gpio-pin $1 to state $2 echo $2 > "$base/gpio$1/value" } echo 'Checking port initialization'; for i in $ports do if [ ! -d "$base$i" ]; then echo "GPIO$i not initialized; exporting and configuring as output" echo $i > "$base/export" echo out > "$base/gpio$i/direction" gpio $i 0 fi done echo "Initialization done" echo "Usage: forwards, stop, backwards" echo "Left track : q, a, z" echo "Right track: w, s, x" echo "Hip hinge : e, d, c" echo "Arms : r, f, v" echo "Gun : t, g" echo "Special keys:" echo "Spacebar = Emergency stop" echo "p = stop all motors and quit application" while true do read -n1 KEY if [ "$KEY" == 'q' ]; then echo 'Left Forwards'; gpio 15 1 gpio 16 0 elif [ "$KEY" == 'a' ]; then echo 'Left Stop'; gpio 15 0 gpio 16 0 elif [ "$KEY" == 'z' ]; then echo 'Left Backwards'; gpio 15 0 gpio 16 1 elif [ "$KEY" == 'w' ]; then echo 'Right Forwards'; gpio 18 1 gpio 19 0 elif [ "$KEY" == 's' ]; then echo 'Right Stop'; gpio 18 0 gpio 19 0 elif [ "$KEY" == 'x' ]; then echo 'Right Backwards'; gpio 18 0 gpio 19 1 elif [ "$KEY" == 'e' ]; then echo 'Bend Forwards'; gpio 20 1 gpio 21 0 elif [ "$KEY" == 'd' ]; then echo 'Bend Stop'; gpio 20 0 gpio 21 0 elif [ "$KEY" == 'c' ]; then echo 'Bend Backwards'; gpio 20 0 gpio 21 1 elif [ "$KEY" == 'r' ]; then echo 'Arms Close'; gpio 22 1 gpio 23 0 elif [ "$KEY" == 'f' ]; then echo 'Arms Stop'; gpio 22 0 gpio 23 0 elif [ "$KEY" == 'v' ]; then echo 'Arms Open'; gpio 22 0 gpio 23 1 elif [ "$KEY" == 't' ]; then echo 'Gun Shoot'; gpio 1 1 elif [ "$KEY" == 'g' ]; then echo 'Gun Stop'; gpio 1 0 elif [ "$KEY" == '' ]; then echo 'STOP'; for i in $ports do gpio $i 0 done elif [ "$KEY" == 'p' ]; then echo 'Quitting and resetting all outputs. Bye!'; for i in $ports do gpio $i 0 done exit else echo "Other key: $KEY"; fi done
Abandoned ideas
Direct Drive control via RaspPI
Machine has been taken apart.. blblblalbla
H-bridge board has all the transistors of the original board on it for 4 full H-bridges that can drive all of the four bi-directional motors.
Original 'direct drive' Layout was as follows , with the connectors of the motors facing you:
- First block, gpio18, w1, gpio4, w7, Left tread
- Second block, gpio17,w0, gpio23,w4, Right treads
- Third block: gpio27,w2, gpio22,w3, hip
- Fourth block: gpio24, w5, gpio25,w6, arms
'Direct Drive' Python code
import RPi.GPIO as gpio import time import pygame from pygame.locals import * gpio.setmode(gpio.BOARD) gpio.setup(7, gpio.OUT) gpio.setup(11, gpio.OUT) gpio.setup(13, gpio.OUT) gpio.setup(15, gpio.OUT) gpio.output(7, True) gpio.output(11, True) while True: gpio.output(13, True) gpio.output(15, False) time.sleep(2) gpio.output(13, False) gpio.output(15, True) time.sleep(2)
pygame.init() screen = pygame.display.set_mode((640,480)) pygame.display.set_caption("Pygame") pygame.mouse.set_visible(0) done = False enable = 0 direction = 0 while not done: for event in pygame.event.get(): if(event.type==KEYDOWN): print event if(event.key==273): direction=1; enable=1; if(event.key==274): direction=-1; enable=1; if(event.type==KEYUP): print event if(event.key==273): direction=0; enable=0; if(event.key==274): direction=0; enable=0; if(enable==1): if(direction==1): print "Forwards" gpio.output(13, False) gpio.output(15, True) if(direction==-1): print "Backwards" gpio.output(13, True) gpio.output(15, False) else: gpio.output(13, False) gpio.output(15, False)
With this code-shim you can test the function of your code even without having RPi.GPIO loaded (like on most PC's)
Note it doesnt implement all things yet; but enough to work for this project currently.
Working on a shift-register help-library
class FAKEGPIO: def __init__(self): self.BOARD=1 self.OUT=0 self.IN=1 def setmode(self,variant): print "Call: setmode: variant -> " + str(variant) def setup(self,pin, type): print "Call: setup : pin -> " + str(pin) + ", type -> " + str(type) def output(self,pin,state): print "Out:" + str(pin) + "-> " + str(state) gpio = FAKEGPIO()
Shift-Register driving of robot
Since the RaspPI doesnt have much IO and the robot requires 8 output-pins just for the basic motor-control already, we've started looking at driving the outputs using shift-registers instead.
The proto-board has been re-wired to have two shift-registers hosted on it, connected in series, for a total of 16 outputs.
The shift-registers in question are the 74HCT595's that has an 8-bit serial register controlled by 'data' and 'clock' lines, and an 8-bit parallel 'latch' that is controlled by a separate line; allowing you to control at which moment the 8 output pins (output A-H) should reflect the state of the internal 8-bit serial register.
By having the parallel register in between the outputs and the serially controlled register, you are able to change the contents of the serial register at your own leisure without the outputs directly following your changes.
The last control-lines on the 74HCT595 are a 'master reset' pin (active LOW; so connect to HIGH if unused) and a 'Chip enable' (active LOW, connect to GND if not used)
The motor-pins are connected to the first of the two shift-registers; this means that the last 8 bits of data shifted into the 16 bit register-space controls the motors as the first bit shifted in ends up being at the last-most position after 16 clock-cycles.
Shift-register layout | |||||
---|---|---|---|---|---|
Register | Bit | Motor | Direction | ||
1 | 0 | Left Tread | Forwards | ||
1 | Left Tread | Backwards | |||
2 | Right Tread | Forwards | |||
3 | Right Tread | Backwards | |||
4 | Hip | Forwards | |||
5 | Hip | Backwards | |||
6 | Arms | Open | |||
7 | Arms | Close | |||
2 | 0 | TBD | TBD | ||
1 | TBD | TBD | |||
2 | TBD | TBD | |||
3 | TBD | TBD | |||
4 | TBD | TBD | |||
5 | TBD | TBD | |||
6 | TBD | TBD | |||
7 | TBD | TBD |
Software Side
To drive the shift-register, two approaches can be used. Either full 'software bit-banging' or using the SPI-bus on the Raspberry PI to drive (at least) the clock, enable and data-lines of the shift-register. The 'latch' pin of the register needs to be bit-banged as there seems to be no easy way to pull this off with just SPI (unless.. perhaps, doing something smart with the 'enable' line..)
The pure bit-bang approach could use any 3 or 4 GPIO pins on the PI, using the work done on https://projects.drogon.net/raspberry-pi/gpio-examples/ as already used and listed above.
The SPI approach could use the work done on http://www.100randomtasks.com/simple-spi-on-raspberry-pi.
An example snippet of code could be
import RPi.GPIO as gpio import time impport spidev gpio.setmode(GPIO.BOARD) gpio.setup(12,GPIO.OUT) gpio.output(12,False) spi = spidev.SpiDev() spi.open(0,0) while True: resp = spi.xfer2([0x5555]) print resp([0]) gpio.output(12,True) gpio.output(12,False) time.sleep(1) resp = spi.xfer2([0xAAAA]) print resp([0]) gpio.output(12,True) gpio.output(12,False) time.sleep(1)
This will import the right libs, setup pin 12 as the 'latch' pin, initialize the SPI-bus using CE0 as the chip-enable line (open(0,0)), and then write a 0x5555 value to the SPI-bus; two bytes, aka 16 bits. 0x5555 and 0xAAAA are eachothers complements; an alternating pattern of 0 and 1's, starting either with a 0 (0x5555) or a 1 (0xAAAA). The 'resp' variable will be loaded with whatever comes back in on the input at the same time; if you connect MOSI to MISO , you will normally read the same thing you are writing out. You can also connect the H' pin of register2 to MISO; this will allow you to read the PREVIOUS state of the shift-registers back... for no useful reason, however.
Toggling pin 12 up and down should trigger the parallel latch to take the contents of the serial latch and adjust the outputs and then make it safe to adjust the serial register again for the next update.