Tuesday, August 19, 2014

Robo UI - Part 1

This is the beginning of what I expect to become a bit of a larger story: My little robot needs some kind of UI. First and most obviously, because it is supposed to create kind of a map, and I need to display that map somehow. But also, because I will probably need a bunch of additional controls: sensors and motors will need calibration, and I might want to build some manual controls for steering. As mentioned this is going to be a somewhat larger story, and this is why:
The map will not be there all at once. Instead, it will build up gradually as the robot is moving about. I will want both the robot's location (and orientation) as well as the map in progress to be updated continuously. So I guess a little Java-Script coding will be in order, in addition to some server-side code that will export a simple http service. But so far, we are not there yet. Instead, I built a very primitive piece of server code that looks vaguely like this:

RoboRequestHandler - A simple web UI for our Robot

This UI implements simple http and some dynamic content
such as a map and a json protocol for status information

'''
class RoboRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

  def do_GET(self):
    if self.path == "/":
      SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
    elif self.path == "/dyn/map.jpg":
      self.handle_dyn_map()
    else:
      self.send_response(403)
      self.send_header("Content-type", "text/html")
      self.end_headers()
      self.wfile.write("<html><head><title>403 Forbidden.</title></head>")
      self.wfile.write("<body>403 Forbidden.</body></html>")

  def handle_dyn_map(self):
    self.send_response(200)
    self.send_header("Content-type", "image/jpeg")
    self.end_headers()

    i = Image.new("1", (320, 200), color="white")
    draw = ImageDraw.Draw(i)
    draw.ellipse((0,0,319,199), outline="black")

    i.save(self.wfile, format="JPEG")

# Start a web server and return
def start():

  # Listen on standard http port to test privileges
  PORT = 80

  Handler = RoboRequestHandler
  httpd = SocketServer.TCPServer(("", PORT), Handler)

  print "http serving at port", PORT
  thread.start_new_thread(httpd.serve_forever, ())

I use Python's SimpleHttpRequestHandler as a basis for a custom RoboRequestHandler. The reason for doing so is because this way I can easily serve static content by delegating to the base class (it wouldn't be much of a big deal in either case, I know. But why repeat code that's already there?) It seems like a good idea to not give people access to all the files in the robot's software working directory, so we return a 403 response for everything we don't want people to see. We will happily serve an index.html file located in our working directory, though. And contained in that is an image, dynamically generated by the handle_dyn_map method and served as "/dyn/map.jpg". This is all a bit static for now, but I guess you can see where this will be going:

Don't blame me for having come up with the most ugly html page ever. I promise to keep improving on it ;-)

Sunday, August 17, 2014

Software to control the motors

Now, I think I did not yet reveal to you why I chose the particular setup of having an Arduino plus Motorshield v2.0 drive the DC motors of my robot. The answer is surprisingly simple: The Adafruit Motorshield v2 is a pretty awesome piece of hardware that allows you to control up to 4 DC motors, two stepper motors and two servo motors at the same time. Way enough headroom for future ideas. The Motorshield v2.0 is controlled via I2C bus, so I guess in theory I could wire it up to the Raspberry Pi directly. For this first version of my robot, however, I decided to connect it to the Arduino and write a very small piece of Sketch code to run on the ATMega controller. All it will do is to accept motor control commands from its serial interface and pass them on to the motor shield. The Arduino comes with a USB COM port driver chip, so connecting the Arduino to the Raspberry Pi is as simple as plugging in a USB cable and figuring out the device special file to use on the Raspberry Pi side. This is the sketch I put on the Arduino:

/*
  Control code for DC motors using Motor Shield 2
  
  This code reads commands in the form of n{+,-}x\n where
  n is the motor number and x is the desired speed.
  Use '+' to let the motor run forward and '-' to
  let it run backwards.
  
 */
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

const int numMotors = 4;

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *motor[numMotors];

void setup() {
  // initialize serial:
  Serial.begin(115200);
  
  // Initialize motors
  AFMS.begin();
  for (int i = 0; i < numMotors; ++i) {
    motor[i] = AFMS.getMotor(i + 1);
  }    
}

void loop() {
  while (Serial.available()) {
    int motoNum = Serial.parseInt();
    char sign = Serial.read();
    int speed = Serial.parseInt();
    if ((Serial.read() == '\n') && (motoNum < numMotors)) {
       // and execute command
       motor[motoNum]->setSpeed(speed);
       motor[motoNum]->run((sign == '+')?FORWARD:BACKWARD);
       Serial.print("Set motor ");
       Serial.print(motoNum);
       Serial.print(" to ");
       Serial.print(sign);
       Serial.println(speed);
    }
  }
}

The Raspberry Pi counter part is implemented in Python and looks like this:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# import required modules
import serial
import time

# main function
def main():
  comm = serial.Serial("/dev/ttyACM0", 115200)
  while True:
    for m in range(0,4):
      for s in range(0,256):
        comm.write(str(m) + "+" + str(s) + "\n")      
        time.sleep(0.1)

if __name__ == '__main__':
  # call main function
  main()

This will make the robot turn on its motors, one after another, and gradually bring them to full speed.
Not very useful, but I think it shows the trick.


Saturday, August 16, 2014



So we have an ultrasonic sensor now, courtesy of SainSmart. This sensor is particularly straight-forward to use. The only thing to actually take into account is that the GPIO pins of the Raspberry Pi use a signalling voltage of 3.3V, while our sensor produces 5V. So following the excellent instructions at http://www.gtkdb.de/index_36_2272.html, I built a voltage divider, using a screw terminal to fixate the resistors. Running a small Python test script finally confirms that the sensor is working. I've connected it directly to the Raspberry Pi for now as opposed to wiring it up to the Arduino. Let's see how that goes. The sensor's logic is simple: Setting its input to high for about 10 μs will trigger a measurement. You then count the time until its output pin becomes high. Now the time counting piece is what makes me a bit worried as I start doing more complex stuff on the Raspberry Pi. Plan B would be to wire the sensor to the Arduino, whose ATMega controller is capable of dealing with this in real time.

Welcome to my blog

I've been passionate about building a robot for a while. Not just a simple one, but one that would actually be able to do something useful. Lots of ideas have crossed my mind... but I decided to start with something fairly simple: A robot that will be able to track its location, without any prior knowledge of its surroundings. Watch this blog to see what comes out of it, but notice that I'll try to not only cover the essentials, but will report about the practical advancements I make.


A nice supply of batteries, delivering 7.2 V at 3000mAh. The Raspberry Pi I'm planning to use is pretty picky about voltage, so I'm using a voltage regulator to deliver a stable 5V via a custom made USB power-plug cable. To get a USB plug, I recycled a mobile phone charger (be careful with this, the condensators in chargers and power adapters may remain charged for quite a while!)


















While I was at it, I also did some further recycling work: The thingy below used to be a DVD burner. I turned it into a pile of parts, some of which are stepper and servo motors. Just the stuff I might need later on.






And this is the first tangible result: The wiring is pretty much complete at this stage, but so far the robot is missing any kind of sensor at all.