Followers

Recent Posts

Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Wednesday, February 10, 2010

Computers 102

For your amenity, the next installment of "inside your CPU" is avaliable below. If your screen is big enough, you shouldn't even need to scroll. The previous issue can be even be found here. Convenience!!



(If you don't get the joke in the last panel, it is because the "take one down" action will eventually set the zero flag in the status register, but the "pass it around" line will never set the zero flag. The branch instruction can only know about the results of the operation immediately before it, so the gnome will eventually die of alcohol poisoning. THINK OF THE GNOMES!)


In other exciting news, the source code I used to make this video is now avaliable. You can find it here. There are a few improvements and bug fixes.

Friday, January 22, 2010

A puzzle

Boy do I have a good one for you today!

First off, version 0.2 pre-pre-alpha-alpha-etc is available. Please download it and try it out - report any bugs or suggestions in the comments.

The improvements in this update focus on enabling the execution of pre-generated actions, such as those from the robobuilder site. A few are included in the Hunobasic.h header for demonstration purposes. I have also written a few useful functions to transfer data from program memory to working memory (which will probably be the subject of the next update).

In keeping with the philosophy of not trying to rewrite the default firmware, I've broken the procedure for executing an action (which is a series of poses) into much smaller steps, which makes it much more intuitive.

Here is the method of making the robot walk forwards:

        getPGMWordArray((int*)HunoBasic_WalkForward_TrTime, (int*)delay, HUNOBASIC_WALKFORWARD_NUM_ACTION);
//Load transition times

        for(i = 0; i <= HUNOBASIC_WALKFORWARD_NUM_ACTION; i++)
        {
//For every pose in the action,
            getPGMByteArray((char*)HunoBasic_WalkForward_Position[i],(char*)position,16);
//Load the angle for each of the servos
            setPose(position,torque,16);
//Tell each servo to move
            if(i < (HUNOBASIC_WALKFORWARD_NUM_ACTION))
            {
//After each frame, except the last,
                j = delay[i];
                while(j > 0){_delay_ms(1);j--;}

//Pause for the required transition time.... 
//wait... 
//..WTF??
            }
        }

What's up with that last part? Why would you loop ten times and each time delay for 1ms, when you could just delay for 10ms in one go?

Well, here's why - it doesn't work!

Substituting the line with:

_delay_ms(delay[i]);

will pause for a much longer time than it should. If you have a AVR, please test this out for me and confirm it. However, passing a constant value to _delay_ms() works exactly as intended. I've tried it many ways, but this is the only method which works so far.

_delay_ms(25);
works, but
delay[i] = 25; 
_delay_ms(delay[i]);
does not.

Mysterious.

As I've mentioned before, this solution is hacky and unattractive. But I thought I might upload the code (the rest of which is working fine) and see if anyone has any suggestions as to why this might be.

Friday, January 15, 2010

One step back

That wasn't so hard.

I think that this code will be more useful to anyone who is trying to use the robobuilder for educational purposes. My goal is not to rewrite the official firmware, but to let programmers have access to the functionality of the RBC and the wCKs.

Code can be found here. It is in pre-pre-alpha mode at the moment, so use it at your own risk. You might want to read this before you test it out.

Monday, December 21, 2009

Phase 1: Collect underpants.

In the absence of a robot capable of supporting it's own weight, I've started planning my next move.

The purpose of writing my own firmware for this robot is purely academic. I don't have a specific goal in mind for this project, even though I'm sure we would all enjoy a robot which could clean our house, bring you drinks, or make us coffee.

As I develop more and more features, I can share them as code snippets which will help other people with their projects. Of course, when I decide I want to revolutionize the coffee robot industry, I will be adequately prepared.

Now that I have figured out how to use the servos as sensors to get feedback about the robot's position, I plan to implement an inverse kinodynamics engine to enable balance without an accelerometer.

For those who aren't down with the lingo, kinodynamics is a contraction of kinematics and dynamics.

Kinematics is concerned with modelling the behaviour of a system given a physical and geometric configuration. In the case of the humanoid robot, this means calculating the relative location of the arms, legs, feet, etc, simply given the angular position of each of the 16 servos. This can be calculated (relatively) simply using basic (again, not something you'd find next to the funny pages) vector mathematics and trigonometry.

Now, dynamics. Kinematics merely describes the geometry and structure of an object. It's velocity, mass, angular momentum and behaviour when subjected to a force are all in the domain of dynamics. When your robot falls down the stairs in a hilarious but tragic accident, his dynamic behaviour determines how long you will watch in abject horror.

So, a kinodynamic model of a humanoid robot can be used to generate a fairly reliable prediction of the state of a robot at a future instant, given the current state and control space.

Put more simply, this model can be used to calculate "safe" actions, which will result in the robot performing a motion while keeping it's centre of mass over it's feet - in other words, balancing.

These are all quite lofty goals, and right now it looks complex, but I'll outline more details in future posts.

Stay tuned for Phase 2: "???"

Tuesday, December 15, 2009

Saftey First

After what happened last time, I've become significantly more paranoid about damaging my robot.

Recently, while doing some more hacking of the wCK motion timing system, I decided to simply remove the element of risk altogether.


So far, separating the arms and legs from the body has been a good move (tm), and I encourage you to do the same if you start changing low level parts of the code.

You see, I am rapidly running out of those spanner shaped connectors.



I mentioned in a previous post how careless control over pointers can cause your robot to flip out like a hyperactive kid tripping over a beehive. The wCK servos have just over 11 kgFcm of torque, which is more than adequate to cause your robot to tear itself to pieces when a rogue pointer starts issuing random commands. I speak from experience.

Serial transmission of commands to the servos can be performed in two ways. The most common method is to use the "Transmission Buffer Empty" interrupt, to send a new character whenever there is nothing waiting to be sent. A transmission is initiated simply by unmasking the interrupt, and the interrupt is set to disable itself when there is nothing left to send. This is the preferred way of sending RS232 communications, as the interrupt will continually fire as long as as something remains to be sent.

The second method of transmitting the message is to use the "Transmission Complete" interrupt. If enabled, this interrupt fires whenever a character has finished being sent. This requires a single character to be manually loaded to "prime the pump", but transmission simply stops when no more characters are available. Since the next character is sent immediately, tighter control over transmission states can be achieved, such as when using an RS485 protocol.

The wCK servos use an RS485 protocol.

The reason that the robobuilder has a tendency to spaz out is due to two things. Firstly, the transmission buffer is circular. This means that messages can be added to the buffer at the same time as they are being sent. When the pointer gets to the end of the buffer, it simply returns to the beginning and keeps sending until it reaches the end marker.

Secondly, memory is never truly empty. A byte in memory simply contains the last thing that was stored in it. On reset, unallocated space usually contains junk data. Even if you set up your program to zero out your memory before you use it, it will still contain a zero. Therefore, even a small mistake which results in one of the transmission interrupts not being disabled correctly will cause a vast number of arbitrary commands being issued to the servos, causing the aforementioned spastic behavior.

Aaaaanyway, now that you know why it happens, you know how to avoid it. I still recommend detaching your arms and legs while you're testing though.

Thursday, December 10, 2009

Son of Voltron

Enabling the robot to recharge it's own batteries was something I wanted to get done sooner rather than later. One of the primary reasons for this is that it meant I needed to periodically switch back to the default firmware if I wanted to charge up the batteries. The second reason though, was that it was a very appropriate stepping-stone on the way to getting several other features working.

You see, the atmel 128 Analog to Digital Converter has a built in multiplexer which allows the single peripheral to measure up to eight different voltage nodes relative to ground potential or other nodes, at various levels of gain. Even better, these sources and configurations are software selectable by the use of a 5-bit multiplexer.

If all that sounded like technobabble to you, the long and the short of it is that the same basic code which measures the power left in the batteries can be used to get distance readings from the IR sensor, and sound input from the microphone!

But back to the power level sensor. This version of the code uses the debug info LED configuration to report the current level of the battery. If your robot is not plugged in, you can actually see the voltage drop when PF1/2 are pressed and the servos are moving. This is achieved by setting the multiplexer ADMUX to channel ADC1, and enabling the AD conversion complete interrupt in ADCSRA, which saves the voltage to the global location gA2D. It is only occasionally necessary to know the voltage, so conversion requests are triggered manually (as opposed to continuously) by setting the ADSC bit.

Recharging is enabled by setting bit 4 on PORTB, which digitally connects the battery to the external power source. Pressing and holding PF1 and PF2 for 2.5 seconds enters recharge mode, which trickle charges the NiMH batteries. Because I'm paranoid after the previous incident, the default setting is to only charge the robot for 10 seconds at a time. If you want to fully charge the batteries, pressing PF1 while the robot is charging will keep the batteries charging indefinitely.

"But Alex!" hear you cry. "If you can measure the voltage, why not simply disable charging when it's maxed out?"

Unfortunately, it's not that simple. When the external power source is present, the RBC automatically changes over the V++ supply to the power pack and becomes the voltage source. This means that whenever the plug is in, the value of gA2D will be that of the power source - even when the battery is low. I haven't figured out a way around this - you might also have noticed that there is no automatic recharge cut-off for the default firmware either.

The code is here. I'm looking for a better hosting service, but in the meantime, remember the click the "free" tab.

Monday, November 30, 2009

Debugging robots for fun and profit

More astute readers who followed the procedure in my last post may have noticed that it didn't actually work.

That's not to say that the instructions were wrong - those are still the steps to build the robobuilder C source files and upload them to the RBC. The problem was that the code which I linked doesn't actually do what it says on the side of the box, so to speak.

The execution of the code posted on the robosavvy forum (which is primarily an english adaptation of the official code) should result in the robot performing the RIGHTPUNCH action when the PF1 button is pressed. However, when the files are built and installed on the robot, no amount of button pressing will elicit a response from the robot.

I'm working on producing an updated version of the code which is nice and flexible, which I will upload here when I'm satisfied with it. In the meantime, for those of you playing along at home, I thought I would share a quick debugging procedure known only to a secluded group of embedded systems programmers.

There are on-chip debuggers available for the AVR family of microcontrollers, however I don't have one. There are also situations when the debugger causes more problems than it solves - ie, by hogging certain pins on the board, or restricting the flow of real-time interrupts. The poor mans solution to this is to set up a quick method of stepping through your program and seeing where it gets to and where it gets stuck. The easiest way of allowing your program to communicate back to you is using the onboard LEDs.

#include "macro.h"
#include "main.h"

WORD gDebugMode = 1;

void Debug(BYTE code)
{
if(gDebugMode)
{
CHK_BIT7(code)? RUN_LED2_ON : RUN_LED2_OFF; //Green
CHK_BIT6(code)? PF1_LED2_ON : PF1_LED2_OFF; //Red
CHK_BIT5(code)? PWR_LED2_ON : PWR_LED2_OFF; //Green
CHK_BIT4(code)? PWR_LED1_ON : PWR_LED1_OFF; //Red
CHK_BIT3(code)? PF1_LED1_ON : PF1_LED1_OFF; //Blue
CHK_BIT2(code)? RUN_LED1_ON : RUN_LED1_OFF; //Blue
CHK_BIT1(code)? PF2_LED_ON : PF2_LED_OFF; //Yellow
CHK_BIT0(code)? ERR_LED_ON : ERR_LED_OFF; //Red
}
return;
}

The input to this function is just a number you can assign to a particular error/state code: ie, Debug(3) would have the error and PF1 LEDs lit, while DEBUG(0) turns all the LEDs off. Combined with the use of the _delay_ms(1000) command, you can sprinkle these throughout problem areas and observe how your code branches or if it gets stuck anywhere.


The ordering of the LEDs may seem arbitrary, and it can be a little hard because the ordering zig-zags upwards from LSB (error) to MSB (power), and then zigs back down again. The reasoning behind this is because I wanted to make the lower numbers easier to read, since three of the LEDs have two possible colours. Hence, it's just normal binary for error values up to 2^5 = 32, and only mildly more complicated for bigger numbers.

Wednesday, November 25, 2009

C is for cookie

The robobuilder kit comes with a suite of tools which can be used to easily create poses and moves. Unfortunately, the overall impression they gave me was that they were useful for little more than making the robot dance. There were some useful features, such as being able to record the current pose of the robot and save it to a file, so I will probably play around with them later.

One of the most attractive features of the robobuilder system is that the firmware can be reprogrammed in C. If that doesn't send shivers down your spine, the rest of this post will probably be over your head. Go watch tv until the grown-ups are finished talking.

The tools to customise the robobuilder firmware are available for download. It comes with a 70 page PDF tutorial which walks you through the process of using the CodeVisionAVR IDE to customise the example builds included. The CodeVision IDE is not free, but I would recommend following the official procedure if: a) You are new to the C language or microprocessor programming and b) you use Windows.

If, like me, you fall into neither of the above categories, I suggest you save your money and have a go at using the avr-gcc compiler and/or WinAVR (which is just a Windows implementation of avr-gcc).

But first, a short rant.

When did motherboard manufacturers stop including serial ports?! I know that they are an aging technology, but they are damn useful. And you, robobuilder - you knew this all along but you didn't ship with a USB adapter or anything. I'm very disappointed. So here I am, forced to write code on the only computer in the house with a serial port - an old Dell Latitude D600. It runs Ubuntu 9.04 and was being used as a media centre PC, but I've since commandeered it in the name of the robot uprising. FYI, all of the robobuilder tools and software will run under WINE.

If you are compiling your code in linux, you will need to install avr-gcc and avr-libc. For reasons unknown, avr-gcc will not detect that avr-libc is installed and include it's headers when you compile. If you get an error message like:

error: avr/io.h: No such file or directory

You may need to copy the contents of /usr/lib/avr/include to /usr/lib/gcc/avr/4.3.2/include.

This robosavvy thread contains a port of the official robobuilder example source files which use the standard avr-gcc headers (also, comments in english!). Details about converting headers to gcc can be found at the avr-libc page. This is the skeleton code you need to edit. Use the command:

avr-gcc -mmcu=atmega128 -I. -g -Wall -Os -c main.c

to compile the source. I do recommend the -Os flag, since there is only 128kB of flash memory on the chip. You may still get some error messages at this stage - I did.

The names of header files are case sensitive in gcc, so check that the names of the #includes match the filenames:

#include "macro.h"
#include "main.h"
#include "comm.h"
#include "dio.h"
#include "math.h"

This should get rid of the remaining compile errors. Next you must link the files together using:

avr-gcc -o main.elf -mmcu=atmega128 main.o comm.o dio.o

If you get linker errors at this stage, check that you used the same -mmcu values for both compiling and linking (otherwise, gcc will try to link to code intended for different platforms).The final step is to convert the instructions to hex using the command:

avr-objcopy -j .text -O ihex main.elf main.hex

The .hex file created from this step is the actual firmware which will be written to the atmega128 chip. To upload it to the robot, simply run the "RBC Firmware update tool", and connect the robot using the (shudder) serial cable. Select the appropriate .hex file, and click the "Click Here" button. Now, press the reset button on the robot, and let the download finish.

To start the custom program, press the PF1 button. BEWARE! If you've made a mistake in your code, your robot will spaz out like an epileptic kid during a fire drill! To prevent damage itself and nearby life forms, either hold it by the head or make sure it is on the floor before testing it. Also note that unless you've specifically prevented such actions, the robot has no idea of it's own kinematics - meaning that it is quite likley to try and put it's arms through it's body when trying to scratch it's back. Be ready to kill the power if you want to prevent damage to the servos.

Now that I have some skeleton code to play with, over the next few days I'll experiment with various useful functions of the chip, like loading pre-built motions and getting data from the IR remote.