Friday, June 24, 2011

TGIMBOEJ #2

With all the craziness of the AVC, I totally forgot to post about the second TGIMBOEJ I received. It was chock full of all kinds of neato bits.

Added: Some memory cards, junked micro helicopters, various vintage passives and transistors, switches, hair drier blower, 6v wall wart

Took: micro heli controller, some jumpers, mini embedded multimeter thingy, binding posts, and a couple other things I forgot about


Lots of great stuff still in there.

Tuesday, June 21, 2011

Tools: Equation Editor (take 2)

[let's try this again...*ahem*]

A couple months ago I found this neato equation editor/creator that generates a nice equation image. Handy for us technical bloggers. I've already used it on several posts. Enjoy!

http://www.codecogs.com/latex/eqneditor.php

Sorry about the mishap yesterday.

Friday, June 17, 2011

Generate a Clock Signal with AVR ATmega

AVR-generated clock signal
Several times lately my MCU needed to generate a clock signal to interface with a device, such as a camera, or a serial interface on an analog to digital converter (ADC).

Rather than bit-banging the clock signal, let the MCU's timer hardware do the work, freeing up cycles for real code.

Here's how to generate a simple, 50% duty cycle pulse train, aka clock signal, using an AVR MCU. For this experiment I used an ATmega328P but any of the AVR chips that support a 16-bit Timer1 should do.

I wanted a 500kHz clock signal. To generate it, the MCU must toggle an output pin, PB1 aka OC1A at 1MHz, or 1/16 the MCU's 16MHz clock, for a total period of 2us (500kHz).

Timer1 provides a mode called Clear Timer on Compare Match (CTC). The timer register, TCNT1 counts from 0 up to the value in the Output Compare Register, in this case OCR1A (to go along with the OC1A output). When TCNT1 == OCR1A, the MCU resets TCNT1 and starts counting again.

Ok, let's get started with the code.

Though we probably don't need to, why not initialize the counter:

TCNT1=0;

To run the timer at 1MHz, we need to divide the MCU clock by 16. Using a prescaler value of 1, we simply set the output compare register to 15, since the timer counts from 0-15 or 16 ticks.


So we come up with:

OCR1A = 15;

Note that we could also have used a prescaler value of 8 and an OCR1A of 1.

Now it's time to set some mode bits. The AVR has three Timer/Counter Control Registers for timer 1: TCCR1A, TCCR1B and, you guessed it, TCCR1C.  In TCCR1A, two bits control the behavior of OC1A when the timer matches OCR1A. Those two bits are COM1A1 and COM1A0. When set to 01, OC1A is toggled when there's a compare match.

TCCR1A |= (1<<COM1A0);

To tell Timer1 to operate in CTC mode, we set bits WGM13, WGM12, WGM11, WGM10 across two control registers. Actually for CTC mode, WGM12=1 and the rest are 0 (initial value on powerup)

TCCR1B |= (1<<WGM12);

To configure Timer1 prescaling, set bits CS12, CS11 and CS10 in TCCR1B. For no prescaling, use 001, respectively. That is, set CS10=1

TCCR1B |= (1<<CS10);

The only thing left to do is to enable the OC1A (PB1) pin for output

DDRB |= _BV(1);

Putting it all together, here's the code that generates the clock signal in the picture above.

TCNT1=0;
OCR1A = 15;
TCCR1A |= (1<<COM1A0);
TCCR1B |= (1<<CS10) | (1<<WGM12);
DDRB |= _BV(1);


Finally, if you want to sync your code to the rising and falling clock edge, check the TIFR1 register for the OCF1A flag:

if ((TIFR1 & _BV(1)) == _BV(1)) {
// do something here
}


Or, you can sync your code to a high or low clock value by reading PB1 from the PINB register.  The MCU can also be set up to call an interrupt service routine whenever there's a match.

Friday, June 10, 2011

Fast Robots are Hard to Build

Back when I was delusional enough to think I should try to actually win the 2011 Sparkfun AVC, I realized I needed a fast robot. The 2010 winning time worked out to a speed of something like 5mph while the 2011 winning speeds were a little over 10 mph, about what I was aiming for, myself.

I wonder what the winning speed will be in 2012, assuming no changes to the rules? 20mph? 30? more? Several of the robots entered this year were capable of running at more than 30mph. A Traxxas Rustler can peak out at 35mph with a 7-cell battery and a large pinion gear.  The VXL version with LiPo and brushless motor can run 60mph.

But, speed without proper control simply won't get it done.

One of my big goof-ups in building Pokey, the firefighting robot, was that I didn't carefully think about speed, timing, and sensor range. The faster your robot is, the more carefully you need to deal with timing, speed, physics, and sensor range.  Here's what I learned from that failed experiment.

Sensor Range

My AVC robot was originally designed to be speedy (10-20mph), but the 2011 AVC incorporated giant barrel obstacles to detect and avoid.

Imagine dodging boulders on the highway while driving 100mph--in a fog with 10 foot visibility! No way. Sensor range has to be long enough to allow the robot to react to obstacles.

I considered several long range sensor options.  To illustrate the issues: one Sharp IR ranger has a range extending to 150cm.  The MaxBotics LV-EZ1 sonar extends to 6.45m at best. Let's say the robot runs at 8mph or ~358 cm/s. That means the robot will only have 419ms to react to the IR range but a full 1800ms for sonar.

The processor can react within a few microseconds, but... there's the steering servo to consider. It's comparatively slow, moving through 60 degrees in 220 ms.  That takes up a whopping 52% of the entire reaction time for the IR but only 12% for the sonar. The time required for tires to grip and suspension to load before turning rate increases may also be a factor.

And what if you want to double your speed to 16mph?  Or double again to 32?  And from hands-on experimentation, sonar readings may not be entirely reliable at > 6m.

I considered slowing down when an obstacle is detected. Doing so would buy more time for the robot to react and change course. Someday when the robot can actually detect an obstacle, I'll try this. :)

Bottom line, if you want to go fast, you better look pretty far ahead. In addition to 6m sonar, there are some other long range options. There's a Sharp IR sensor, the R316-GP2Y0A710YK, that can reach out to about 5.5 meters.

I also considered machine vision to extend object detection range out past 6m. As you might imagine, the learning curve in the area of machine vision is pretty steep, so that's why I tried a CMUcam1. It was starting to look promising until I ran into other issues.

If I had won the lotto I would've spent $9095 of my winnings on a MESA 10m SwissRanger. :)  Ah well.

Timing

System timing is pretty important too.  At one point, Pokey's infrared ranger sensors updated 20 times a second for wall-following, while steering correction happened at least an order of magnitude faster. The robot would make a steering correction to avoid a wall, but wouldn't wait for the next sensor update.  It'd turn the wheel but nothing would (seem to) happen!  So it'd turn the wheel harder... bad news.

This time around, I carefully planned out the 'schedule' for sensor update and control output to ensure a sane approach to course correction.  I checked the EZ1 sonar, IR Rangers, and IMU sensors at a 20Hz rate and made steering corrections every 100ms. A vision system could be run at 10-20Hz to match other sensor scheduling. Depending on the module, GPS updates at 1-10Hz. Even at 5Hz, that's a long period of time without data so dead reckoning (heading + odometry) has to fill in.

Also worth considering is the update rate versus vehicle speed and sensor range. Let's say the robot detects something at 6 meters. How many more meters will the robot drive until the next ranger update?  At 20Hz and 8mph, it'll be 1/20th of 358cm = 18cm. How many degrees can the steering servo turn in this time?  Only 0.18 degrees in this case.  The robot better not be impatient waiting for something to change.

To the MCU, it's analogous to a person steering a giant battleship.

Physics

Pokey was heavy and took time to change velocity (direction or speed) but was also fast. By the time it detected the need to stop or turn, it overshot its mark considerably.  One can largely ignore momentum in a slow and/or light robot.

As you know by now, Data Bus is at its core an RC truck. Based on testing (read: playing with it) in front of the house, it has all the expected handling limits of any car.  It can't take a corner at straightaway speed.  It takes time to stop and start.  It takes time to change directions even after the steering is turned.  If you turn the wheel too fast at full speed, the front tires can break free and lose traction.

All these physical limitations and nuances have to be considered in the design if the robot is to run reliably and quickly. I took a simple, two-fold approach. First, handling improvements in the form of suspension tuning and better tires, tricks taken from my SCCA Autocross days.

Second, run the car comfortably below its handling limits. Wider turns and slower speeds, in other words. A slightly more sophisticated approach to reach higher average speeds would involve slowing or braking before turns.

A far more sophisticated approach was taken by Project240, limiting lateral acceleration, tracking to different speeds around the course (e.g., arriving at waypoint turns). Quite impressive.

My simple approach worked in that the robot could take corners pretty sharply, entering them around 10-12mph, despite the sensor mast that degraded handling considerably. But the solution was far from maximizing speed and handling potential.

Results
In the end, despite actual navigation issues, the scheduling of sensor, steering, and gps updates worked well, and the vehicle's handling easily supported the meager demands placed on it. So at least something worked out ok.

Friday, June 3, 2011

Diagonal components in Eagle

Oooh! Diagonal chips are so cool!
(Picture from Sparkfun)
I always thought it was neat when little surface mount chips like AVRs or ARMs were mounted diagonally on breakout boards like on the Arduino Mini you can get from Sparkfun. It makes routing easier in some circumstances.

If only Eagle CAD could do that...

But it can!

You can position ANY component on your PCB to any arbitrary angle. And it's really easy. Here's how.

Let's look at this pesky bypass capacitor. There's no room between these traces for it. The board is jam packed. But hold on. If you mount the component diagonally it just might fit!


In the Board view, right click on the component in question and select Properties from the contextual menu.


In the properties dialog, simply edit the Angle field. For diagonal placement, enter 45°, 135°, 225°, or 315°. It doesn't matter as you can always rotate the component 90° at a time as before. Click ok, and then rotate the component as needed.

There, that's better. Diagonal fits!

Voila, the little capacitor can now be squeezed in between these traces. Moving things around just a little bit, I was able to eliminate the DRC error (white box/line) and all was hunky dory.