Idea for cheap, basic visual movement perception

I’ve been thinking about ways to increase the amount of information entering my Orangutan over the fewest lines:

An R/2R resistor ladder with each input bit being fed from a set of 6 light-dependent resistors placed at known points around the robot. Now, between each LDR and its R/2R ladder input would sit a trim pot and a Schmitt trigger to convert the analogue signal from the LDRs to a nice crisp +v. Feed the output from this makeshift DAC into a single I/O line set to analogue, and from a 6-bit R/2R ladder you get 32 unique values for all combinations of sensor activation - forthe cost of just one input line. In this way, the robot could very cheaply tell not only the direction of light and dark, but how it changes over time - VERY basic visual movement perception, in other words. If it’s stationary, changes in light direction mean the light is moving. If the robot is moving, there’s a good possibility that the light is stationary.

The question is have I just re-invented the wheel or is this interesting?

Hmm. The system you’ve described could let you get up to 48 different sensors, but I think that it would be pretty expensive to get the resistor network perfectly calibrated and build all of those Schmitt triggers. You would only get digital values out of the sensors, which means that the light levels would have to be exactly right for it to be sensitive to any changes. How many sensors do you want to put on your robot, and have you tested them out yet? If you really need so many, I’d say that you should either get some analog multiplexers or throw on a few baby orangutans as secondary processors that would communicate with the main processor over some kind of serial line. Either way, you could get the full 10-bit resolution on each sensor without having to mess around too much with the electronics.

I read over the description of your project in your earlier post, and I’m still wondering whether there is any reason to go to assembly for what you are doing. It sounded like you are trying to program in some very simple behaviors - so why do you anticipate using up all of the flash memory of your Orangutan? Have you started programming the board yet, to get a feel for how much space things take up?

Hi Paul, thanks for your reply.

Before I begin my reply, it might help to know that I learned electronics 25 years ago in physics class. I used to be good with a breadboard, but that was a long time ago. I’ve forgotten tons of things, so I’m basically learning what I need as I go. If I appear to be a clueless amateur, that’s because I am! So please bear with me.

The idea of a directional light detector was really just an idea for reliably squeezing as much digital data as possible down a single analogue port. I nearly described a digital touch sensor array instead, which with hindsight would have been more appropriate. To avoid having to build Schmitt triggers from scratch, I thought maybe I could just use a cheap trigger chip containing 6 devices to buffer the inputs to the ladder, hence the idea of having 6 inputs. Would that work or am I wide of the mark?

I hear what you say about calibrating the R/2R ladder in hardware, but I think I have a solution: do it in software. Simply ask the operator to activate a single sensor. Test the level on the I/O line, store and use it as a reference. Do it fro each sensor in turn, and if you subsequently see one of the levels on the line again, you know which sensor generated it. But by calibrating in software, you also gain the ability to test a particular sensor’s activation at times when others are also activated. This works because each pattern of sensors firing is unique. In binary, you’d simply mask out the other bits. Similarly, if you sum all the reference values except the one for the sensor you’re testing for activation and subtract this total from the current value on the line, if the number you’re left with is the same as the reference value of the sensor whose activation you’re testing, it must be activated, otherwise it’s not activated. If you have N sensors, you only have to do N-1 such checks, making it very efficient.

It might help if I share some of the design details for the system I’m developing. At the centre of everything is a data structure holding the machine’s current state, acting as its long-term memory and basic personality. This is a social, behaviour-driven device, so the major variables quantify abstract concepts such as boredom, happiness, boldness, fear, and so on. The design has about 20 of them.

Running on the machine will be four pieces of software. Program 1 samples the inputs, decodes them and decides how to update the machine’s major state variables. In doing so, it also takes the machine’s current state into consideration so that it’s not just responding to simple cause and effect. This program also has it’s own variables (called deltas), which it uses to determine how the current inputs differ from the previous ones. This cheaply gives it extra time-based information (including how many duty cycles since a signal last came in on a port), which it also uses when deciding how to update the internal state.

Program 2 has the most complex logic of the four. It examines the machine’s current state and decides what to do in response. First, it tests for state variables with high values that indicate things it must deal with immediately (such as extreme fear or intense boredom at not being interacted with for too long). To decide if one of the state variables is sufficiently high to require immediate attention, the program consults further minor state variables called triggers that define these levels (by storing them rather than hard coding them, these too can be modified as the machine runs). If the program finds no such conditions, it’s free to think about combinations of state variables that when taken together code for a more complex, “higher” emotions. Supposing, for instance that the state variables show that the machine has fairly high values for both happy and scared. Combined, isn’t that a feeling of exhilaration? It’s different from plain happy, and certainly very different from being simply sacred. There are plenty of other combinations that make interesting sense, too. The point is that over the course of its existence, interaction creates this slowly evolving, derived overall internal state. The program evaluates such conditions in a strict order (I’m toying with the idea of allowing it to alter the order, but I’m not yet sure whether this would be meaningful). Once program 2 decides what to do, it simply sets a variable called the response code and begins its duty cycle again.

Program 3 has a data structure consisting of lists of actions. The lists are currently up to 8 actions long, and each entry is also parameterised for greater flexibility. There are several such action lists associated with each response code, and each member of a group is also ranked against the other members. The pool of lists program 3 selects from is dictated by the response code sent to it by program 2. The actual list is decided by the rank plus a small random value, so that rather than simply selecting the same list every time, there’s some flexibility. For each entry in the list it chooses, program 3 then calls an associated subroutine that in turn carries it out using the entry’s parameters to tailor its actions. In essence, the lists can be seen as operators in a very simple programming language. The parameters associated with each are its operands and might indicate the note length and pitch for the buzzer, a direction to turn in for the motors, the number of repetitions of a more complex behaviour, and so on. It all depends on how creative I can be, though I expect this area of the code to evolve and grow greatly. When the system is initially loaded into the machine, these lists all have sensible defaults. For instance, if the state variables indicate that the machine is significantly happy more than anything, program 2 may tell program 3 to make it perform one of the action lists from the happy group (the list in question might implement a “happy” dance, as a silly example). But what if you don’t like the machine doing the same happy dance each the time it decides it’s happy? My solution is to have it learn symbolically from the user (I originally wrote “owner” but that felt somehow weird).

I’m hoping to communicate using the RC hardware from one of those tiny novelty cars you get in toyshops. I believe these encode and transmit 4 binary states – left, right, forwards and backwards. By redefining what the states mean, two of them can be remapped to mean “YES” and “NO”. If you press the NO key, program 1 reads the input, decodes it and passes it to program 3 (as well as updating the machine’s state variables to reflect your disapproval of its antics. After all, everything is potential information). In turn, program 3 decrements the rank of the action list it last ran (the one you didn’t like), meaning it’s less likely to select it next time. If the list gains a sufficiently low rank, program 3 simply overwrites it using a combination of actions from the other lists plus a few slight randomisations (I’m undecided whether to have the list play out there and then, or whether to simply give it a high rank and let it emerge later). If you press the GOOD key, however, program 3 will assume you liked the last list it performed, and will increment its rank.

So, though the low level behaviours exhibited by the machine could be simple in themselves, when combined into action lists and parameterised they become more interesting. By chastising the machine for acting in a way that you deem “bad”, it learns not to do something, and will re-write the associated list if you don’t like it sufficiently. There are two states left on the RC transmitter. Let’s call one of them “MODIFY”. If you press this button, program 3, will randomly modify one entry in the last action list it ran. Next time the list runs, it’ll be different, though this is a better candidate for running immediately so that you can add further modifications to it. Let’s call the final state “STOP”. If you press this while program 3 is running an action list it will simply stop what it’s doing. Press NO, and it’ll also demote the list. After all, you may just not want the machine to act in a way that’s inappropriate to the current situation, not modify its behaviour permanently.

Finally, program 4 is the housekeeping and executive program. It initialises the system and “normalises” the major state variables, which means decrementing them by set amounts (also defined in the state data structure and modifiable at any time) until they attain a base level (also modifiable). Just as in nature, it’s unusual for someone to remain, say, happy all the time. It wears off as neurotransmitters are reabsorbed. This is program 4’s job. If program 1 isn’t updating the value of a state variable in response to external stimuli, program 4 ensures that it tends to calm down again. After all, continuous happy dances would get boring after a while – even for the damned robot! Program 4 will also let you into service mode where you can calibrate the sensors as described above, and explicitly enter new actions lists directly using the Orangutan’s onboard LCD and push buttons.

As you can imagine, there’s more to the whole thing than this brief description even though this description has taken several hours to type and edit, but I hope you can see that I really will need access to every byte I can get my hands on. I already have the basic state structure and the duty cycle for program 4 worked out, coded up and tested in AVRstudio 4, along with the subroutines that implement the normalisation functionality. I now need to decide on and build the sensors I want because that will define the exact functionality of program 1 in greater detail. I also have to decide on the outputs I want, and how I’m going to express them. With sound and power already laid on, I’m currently leaning towards an external 2-bit demux giving 4 binary outs, and two analogue lines. Again, it’s an attempt to squeeze the most data through the fewest ports.

I know I could simply go wild with baby-Os, but the only hard and fast design ethos I’m following for this project is to keep the hardware costs to the absolute minimum and do the most possible in software.

Here’s the problem I see with calibrating your R/2R ladder in software: there is a very high chance that certain combinations of sensor readings will give the same analog values as others. With 10-bit A/D, there are 1024 possible values that you can read for the voltage, and there are 2^6=64 possible states for your sensor array. At first, it seems like there is plenty of room for 64 states to map into 1024 values without overlap. However, if you don’t calibrate your resistors, the values will become somewhat randomly distributed. If you assume they are randomly distributed, about 1/16 of the sensor states, 4 different states, will map to values that have the same 10-bit value. If you have to restrict yourself to, say 8-bit A/D to avoid susceptibility to noise, 1/4 of the states will become indistinguishable from others due to this kind of overlap. And it’s not like the indistinguishable readings will be similar: for example, 100000 is likely to give the same analog value as 011111. Maybe you could overcome these problems by looking for the changes instead of the actual values, but switch bouncing and other issues are going to make that quite a challenge. I still think that compared to building and calibrating the R/2R + Schmitt Trigger circuit, an analog or digital multiplexer is going to give you much better results.

On the output side, you said you would use a 2-bit demux, but is this going to save you much? It seems like you’d have to use 3 lines to get 4 outputs. What kind of outputs are you going to drive? If it’s things like LEDs and buzzers, maybe you would do better use the high-impedance state of the AVR’s output pins to your advantage and connect them between various pairs of I/O pins, of which you activate just two at a time, one high, one low. 3 pins would give you 6 possible high/low pairs; 4 would give 12 outputs. Does that make any sense?

For both input and output, adding secondary AVRs would give you a much simpler and more flexible solution, allowing an arbitrary number of I/O lines over just one or two wires. They certainly don’t have to be baby orangutans, if you’re trying to save money and don’t want extra motor outputs!

What you’ve described so far sounds like a robot that runs on pure emotion, with no logical side to it at all. Do you think that any combinations of emotions would ever cause the robot to, say, follow a line? From personal experience, I know that falling is scary, but it’s definitely not a constant feeling of fear that causes me to keep my balance while walking. So, my question is, is the goal of this project purely to explore emotions, or do you have an idea about how to combine these emotions with some kind of logical behaviors?

More interesting is the idea that emotional responses could be learned in this way. I guess that I always of emotions as a kind of trigger for learning - the pain of falling causes me to learn to not take actions that lead to a fall. Learning is itself the action that I take in response to pain, and as far as I know that’s been my response to pain since I was a baby. What you are talking about is learning to change the response to pain. Is this something you thought of yourself, or is there some theory of psychology that this is based on?

It would be interesting to see the list of all 20 different emotions that you are going to try to put into the robot! This reminds me of an experience from a long time ago…I built a robot for a contest that had a single emotional variable that you could call “happiness” or “pain”, depending on which side you measured it from. The variable would go up or down in response to its success or failure to proceed with the different steps in the contest, and increasing values of pain would cause it to take more are more desperate measures. During one match, my robot encountered its competitor head on, and the two pushed motionlessly against each other; neither could budge the other. This lasted for ten seconds or so, until a pain threshold was passed, and my robot apparently became enraged, repeatedly backing up and ramming its competitor, who wisely reacted to the sudden freedom to move by turning right and running away from the danger. My robot must have been hurt enough by the encounter to feel serious anger towards the other, since it somehow - this was not intentionally programmed! - decided to turn left and continue ramming it form behind. The audience went absolutely wild at this display of robot rage, as the competitor fell to pieces and mine won the match. All this from a single, slowly incrementing variable! The experience went a long way toward convincing me that the emotion, which we usually regard as a uniquely human trait, is something that computers will master quite easily when it’s necessary. I’ll be happy to hear more about how your robot turns out!

Do you have your Orangutan yet? What are your plans for a chassis and sensors?

1 Like

Hi again. I’ve been building and testing a 4-bit prototype for the ladder (I only had 4 LEDs of the same rating), and I thought I’d update you on progress.Basically, it reliably detects single set bits. Here’s the schematic:

I wrote a little program to test the AD value for each line (I have LEDs attached, so I took readings both with and without) and graphed the results:

And here’s the finished prototype running. The resistors are standard 5% ones. The orange wire is ground, yellow is C0, and green is Vcc - used to send a bit high:

And finally, here’s some very poor quality C used to test it all:

#include <pololu/orangutan.h>
#define MAX_ADC_ERROR 3
#define SETUP_DELAY 2500

int current_sensor_array_value();
int value_in_range();
int main()
{
	int a, bit0, bit1, bit2, bit3;

	set_analog_mode(10);
	clear();
	play_note(C(5), 250, 10);	
	print("Bit0   ");
	delay_ms(SETUP_DELAY);
	bit0=current_sensor_array_value();
	lcd_goto_xy(0,0);
	print("Bit0=");
	print_long(bit0);
	delay_ms(SETUP_DELAY); 

	clear();
	play_note(D(5), 250, 10);	
	print("Bit1   ");
	delay_ms(SETUP_DELAY);
	bit1=current_sensor_array_value();
	lcd_goto_xy(0,0);
	print("Bit1=");
	print_long(bit1);
	delay_ms(SETUP_DELAY); 

	clear();
	play_note(E(5), 250, 10);	
	print("Bit2   ");
	delay_ms(SETUP_DELAY);
	bit2=current_sensor_array_value();
	lcd_goto_xy(0,0);
	print("Bit2=");
	print_long(bit2);
	delay_ms(SETUP_DELAY); 

	clear();
	play_note(F(5), 250, 10);	
	print("Bit3   ");
	delay_ms(SETUP_DELAY);
	bit3=current_sensor_array_value();
	lcd_goto_xy(0,0);
	print("Bit3=");
	print_long(bit3);
	delay_ms(SETUP_DELAY); 
	play_note(C(6),250,10);
	
	while (1) {
		clear();
		a=current_sensor_array_value();
		if (value_in_range(a,bit0)) {
			print("Bit0 set");
		}
		if (value_in_range(a,bit1)) {
			print("Bit1 set");
		}
		if (value_in_range(a,bit2)) {
			print("Bit2 set");
		}
		if (value_in_range(a,bit3)) {
			print("Bit3 set");
		}
		delay_ms(10);
	}
}

int current_sensor_array_value()
{
	start_analog_conversion(0);
	while (analog_is_converting()){}
	return(analog_conversion_result()<<1);
}	

int value_in_range(int a, int v)
{
	if ((a>v-MAX_ADC_ERROR) && (a<v+MAX_ADC_ERROR)) {
		return 1;
	} else return 0;
}

The four almost identical blocks of code at the start (bad programmer, naughty programmer, lazy programmer!) simply beep, wait for a default 2.5 seconds while you connect up the indicated bit, display the sampled value, store it as a reference value and move on to the next one. There’s a final high beep to say it’s finished, and the program then simply loops, detecting which bit is being set at any given time, and giving a blank screen if none are set. In sampling the bits in this program, I decided to double the value returned to spread them out, hence the shift.

As you pointed out (thanks!) the current ADC value can be noisy, so I deal with this in the function value_in_range(), which tests for its first argument being sufficiently close to the second (+/- MAX_ADC_ERROR). MAX_ADC_ERROR is currently set at 3, which has given perfect results all day. If noise gets worse as a function of decreasing power, there’s no reason why it can;t self-adjust to compensate - up to the point where there’s not enough power to tell what the hell’s going on!

So there you go. I now need to apply this thinking to the problem of detecting multiple set bits. I can probably use the same range idea on complex ADC values, but this time I think it’s going to be used to return a standard, computed value from a noisy one that can then be used to extract information about individually set bits.

Phew! To the pub, I think…

That’s a great demo that you’ve put together! I like seeing the graphs of the ADC values resulting from each of the bits - it makes it really clear what you are doing. Now, I’d like to see you expand this to include all 16 possible settings of the bits. Since each setting occupies 7 out of the 1024 possible analog values, there is a small chance that two of your values will overlap. I think it’s pretty likely to work with 4 bits. But once you expand it to 6 bits, the 64 possible settings will have to occupy 448 out of the 1024 values, and there’s only a small chance that you won’t have overlap.

The Schmitt trigger is going to make this a lot more complex, but I encourage you to give it a try and post your results here!

Here’s one other idea - for testing the bits, you could use 4 output ports from your Orangutan as inputs to the R/2R ladder. Then the Orangutan could test itself.

Good news!

I’ve added extra bits to the ladder and tested it, and I can safely say that (with the technique expressed in this software at least) it will reliably handle 6 individual bits. Add a 7th and it can’t recognise it, but it does still recognise the first 6 perfectly with a 7th in place. That’s not too shabby. I now need to think in detail about how I implement testing for a single bit amongst others at that resolution.

One thing that might save you even if you have overlapping bits is to look at the changes. If there are multiple possibilities for the bit pattern corresponding to the value you are currently reading, you could pick whichever one requires the fewest number of bits to have changed. What do you think about that?

Hi again,

I’m feeling quite pleased with myself. I’ve solved the problem of reading multiple 1-bit binary sensors (for touch, collision, etc.) over an analogue input using the technique I’m also using to read arrays of analogue sensors.

In my prototype light sensor array, I have 4 simple transistorised LDR circuits independently measuring light levels. I decode a 2-bit address into 4 lines and use this signal to both drive the selected sensor and gate its output simultaneously. The output is fed back into the Orangutan. Dead simple. The prototype will scan the array as fast as it takes the CPU’s ADC to convert the analogue signal. I haven’t taken any exact timings but it’s far faster than the LCD can display!

So, I thought, why not use the same technique to scan an array of 1-bit digital sensors? A 558 quad timer costs peanuts but in monostable mode it’ll nicely buffer 4 sensors for a short time so that I don’t miss very short events. If I select a sensor and get a 1, it’s been activated. With no AD conversion, it should run a lot quicker, too.

I think I’m getting the hang of this…