kig.re Writings on tech, music and life by Konstantin Gredeskoul, aka 'kig'

How It All Started

I've been a software engineer or technical leader for most of my career, which approaches on 20 years (I just recently hit 40 earlier this year). I started coding when I still lived in Kharkov, Ukraine, not too far from where the war is raging right now (a fact I am still finding hard to believe). I wrote my first program sometime in 1988, which was a keyboard driven version of "Paint": you could move a cursor that drew a line on your screen, and save and recall your creations. This was all before the mouse made it over there.

Just around my 40th birthday I wanted to fix a subwoofer that I intended to use for the birthday party that day. It was a Mackie powered subwoofer, about 500W (plenty loud), but it's been broken since the last Burning Man, when it quietly died in the 110'F heat. I opened it up, found some wires that seem inappropriately disconnected, and soldered loose ends to what I thought was the right receptacle with a crappy giveaway soldering iron I received at some Java conference more than a decade ago. I wasted two hours of my life aimlessly trying to "fix" the subwoofer, and while I was doing that a realization dawned on me: I have absolutely zero idea about how any of this electronics actually works... I did remember some facts about resistors and capacitors, but I surely couldn't tell what was what in front of me, how it all fit together, and more importantly: how was I going to fix this thing.

In the end I was not successful in my quest. I've assembled the subwoofer back to it's original form, and it's currently sitting in my garage waiting to be delivered someone with trained hands at JK Sound SF, someone who can actually fix it. But in the meantime, I signed up for a soldering class at the TechShop, and purchased a "pro" level soldering iron – Weller WES51 on Amazon. This is how it all began.

↳ Keep reading …

Back-Seat Driver: Autonomous Robot Maneuvering

BackSeat Driver: Autonomous Vehicle Library for Arduino

Today I am introducing the Back Seat Driver – a library for programming autonomous (or not) arduino based robots. Source Code on Github.

This library provides a convenient non-blocking command API to programmatically drive an autonomous vehicle. Current implementation is aimed at a 2-wheeled robot, with the two Servo motors setup opposite each other. Therefore to move the robot forward (or backward), two Servos need to rotate in the opposite direction (this is certainly true in the current version of the library, but may be more flexible in the future if need arises).

Library Features:

  • Intuitive and easy to read/use API
  • Non-blocking duration-based maneuvers, i.e. "go forward for half a second, then do this..."
  • Un-timed moves, such as "go backward" indefinitely
  • Turn by angle, computed based on the the wheel ratio coefficient that can be adjusted for each size of tire
  • Much more linear speed curve, when mapping from speed in % from 0 to 100, to Servo microseconds. Using arctan() function allows to flatten out uneven Servo speed response curve (see graph in the PDF for Parallax Arduino Robot Shield Kit).

Design Boundary

Imagine an autonomous robot, driving itself around, sensing and reacting to the environment around it. You can imagine yourself shouting robots command, as you see it approach a table: "Stop, turn left and see if it's any better". For any robot to be autonomous this logic must also be implemented in code. But the imagined scenario creates a clear boundary which separates an instructional algorithm that avoids obstacles and determines what the robot does next, from the robot movement library, which simply knows how to spin robot's wheels to achieve a move or a turn. This project, as you probably guessed, is about the latter part.

Non-Blocking Control

Nowhere in the library is the dreaded delay() function called (this is similar to ANSI/C sleep()), and so the program flow is never paused. . The client of the library is able to provide callback functions to be executed at the end of a given maneuver, such as a turn.

As a trade-off, the client is required to periodically call robot->isManeuvering() function, to ensure that all callbacks have a chance to execute and clear, and any maneuvers stopped. If this function is not called frequently enough, turns can go on for longer and thus be wildly inaccurate. This requires more coordination, but provides for a lot of options to execute logic while maneuvers are happening.

Neither does the library use any precious Arduino interrupts.

Hardware Requirements

For an example hardware see DIY kit called "Parallax Arduino Robot Shield" available here: http://www.parallax.com/product/323

Any Arduino card with 2 Servo motors attached would work. For most Servo motors that can move the vehicle you would need a decent power supply. The robot above uses 5 x 1.5V AA batteries for a total of 7.5V and about 1Amp. A dedicated motor shield such as Adafruit Motor Shield V2, would be an excellent choice, but there are many others.

Disclaimer and Invitation to Collaborate

This project is also an eternal learning quest for the author, who only started tinkering with Arduino at the end of June 2014. Therefore please keep in mind that this library is not written by an Arduino expert, although the author does have an extensive software development background in other languages.

Any suggestions or modifications are welcome, and will be considered, discussed and decided on in the issues or pull requests.

Usage

Moving Forward, Backward

The speed value passed into the APIs provided by the library are expected to be always positive, and expressed in percent % of the total max speed of the servos.

    // puts motors into 100% speed, forward motion,
    // and immediately return from the function
    robot.goForward(100);

or

   // go backwards @ 50% speed, for 1 second, and then call
   // turnAround() local function (defined elsewhere)
   robot.goBackward (50, 1000, &turnAround);

   ....

   // somewhere else in the code

   // wait for the any maneuvers to finish
   if (!robot.isManeuvering())  { ... }

Speed

Speed is always passed in a positive integer, from 0 to 100 (expressed in %).

Internal helper converts that to Servo's microseconds PWM timing. In addition we apply arctan() function to transform speed and provide more linear response between speed and RPMs, compared to when specifying microseconds directly.

Duration and Callbacks

Duratation based API calls, such as

// load specific Adapter for our motors
#include <BackSeatDriver_TwoServoAdapter.h>
// now load the main library
#include <BackSeatDriver.h>

// initialize the adapter with two pins assigned to
// the two servos
BackSeatDriver_TwoServoAdapter adapter(13, 12);
// intialize BackSeatDriver itself, passing it the driver.
BackSeatDriver robot(&adapter);
// now we can ask our robot to move...
robot.goForward(100); // move forward at 100% speed

is giving robot instructions that will stop the robot after the duration of time passes (defined in milliseconds from the start of the maneuver).

To remain within design goals of the library, the client should aim at not performing any blocking calls or delays itself, as doing so will make BackSeatDriver inaccurate, and will prohibit from effectively integrating other similar libraries. In the world where many things should be moving at once, nothing in the code path seems worthy of a dead pause of the delay, although for simple tasks it sure makes coding easy.

Example

In this example the client uses Sonar sensor to detect objects ahead. If an object is found, the robot turns left (-)45 degrees, and then immediately checks again for distance. If the distance to the objects ahead is farther than the previous reading, it stays on this route and keeps moving. Otherwise it rotates 90 degrees, to now be at +45 degrees to the original direction. If that direction isn't better than the front, it turns for additional 135 degrees, making it a full 180' degrees from the original direction.

This example algorithm is setup in a just a few lines of code using C/C++ style function pointers used as callbacks at the end of each maneuver, but provide for a pretty effective obstacle avoidance strategy (but albeit a random direction).

// Define the two pins used by the two Servos attached to the wheels (expected to be attached
// in an opposite direction to each other
// load specific Adapter for our motors
#include <BackSeatDriver_TwoServoAdapter.h>

// now load the main library
#include <BackSeatDriver.h>

// initialize the adapter with two pins assigned to the two servos
BackSeatDriver_TwoServoAdapter adapter(13, 12);

// intialize BackSeatDriver itself, passing it the driver.
BackSeatDriver robot(&adapter);

void setup()
{
    robot.attach();
}

void loop()
{
    // The most important check below serves two key purposes:
    //
    //   1. make sure that any existing maneuvering that may be happening should be
    //      finished, and if so stop the robot and execute maneuver's callbacks()
    //
    //   2. avoids doing any instructions until the robot is out of the maneuver.
    //      This could be optional, as many things can be done during the time
    //      robot is maneuvering, perhaps with other arms or sensors.
    //

    if (!bot.isManeuvering()) {

        // this is the default motion
        bot.goForward(100);

        // check distance to objects ahead
        spaceAhead = detectSpaceAhead();
        if (spaceAhead < 50) { // if under < 50cm start manuevering
            // turn left 45 degrees, and when done call the checkLeft() function.
            robot.turn(-45, &checkLeft);
        }
    }
}

void checkLeft() {
    int spaceAfterTurn = spaceAhead();
    if (spaceAfterTurn < spaceAhead)
        bot.turn(90, &checkRight);
}

void checkRight() {
    int spaceAfterTurn = spaceAhead();
    if (spaceAfterTurn < spaceAhead)
        bot.turn(135, NULL);
}

Conclusion

↳ Keep reading …

12-Step Program for Scaling Web Applications on PostgreSQL

On Tuesday night this week Wanelo hosted a monthly meeting of SFPUG - San Francisco PostgreSQL User Group, and I gave a talk that presented a summary to date of Wanelo's performance journey to today. The presentation ended upo being much longer than I originally anticipated, and went on for an hour and a half. Whoops! With over a dozen questions near the end, it felt good to share the tips and tricks that we learned while scaling our app.

↳ Keep reading …

Detangling Business Logic in Rails Apps with PORO Events and Observers

With any Rails app that evolves along with substantial user growth and active feature development, pretty soon a moment comes when there appears to be a decent amount of tangled logic, AKA "technical debt."

↳ Keep reading …

High Read/Write Performance PostgreSQL 9.2 and Joyent Cloud

At Wanelo we are pretty ardent fans of PostgreSQL database server, but try not to be dogmatic about it. 

I have personally used PostgreSQL since version 7.4, dating back to some time in 2003 or 4. I was always impressed with how easy it was to get PostgreSQL installed on a UNIX system, how quick it was to configure (only two config files to edit), and how simple it was to create and authenticate users.

↳ Keep reading …