25 January 2010
Shifting Gears - iRobot Create
I also got a Command Module (CM) which fits onto the connector. The CM is an Atmega 168 processor that adds some additional I/O. It can be programmed to control the Create. I did so and basically reproduced the wandering behavior of the Create. It move around, bumps into things and turns away from what it hit. I added some additional behaviors such as if it got trapped, i.e. caught in the same place for a period of 10 secs, it would move to extract itself.
I want to do more with robots, such as entering in a RoboMagellan contest. That requires an outdoor capable robot that does a lot more than bump into things. A key component to me is vision. Maybe I could do that with the CM and another processor (like the CMUCam) but I really didn't want to learn YAPE (yet another programming environment).
Around the time I got thinking seriously on this I looked at ITX boards. Then the Fit PC computers became available, specifically the Fit PC Slim. The PC form and wireless sold me on trying to us it. The one drawback might be the processor speed when trying to do vision processing. That is acceptable because the Create with the Slim is a testbed for RoboMagellan where an entirely new, slightly larger platform will be used. By going with the PC as the base there are a large number of possibilities, including laptops and netbooks. If the processor is slow for vision the Create simply won't move as quickly or smoothly.
I have the Slim hooked up to the Create, drawing power, and running most of the behaviors previoiusly implemented with the CM. Once I got the basic threading, serial communications, and Create interface working the behaviors started working within minutes since they ported easily from the CM versions. All the code is C++. Threading and serial port routines are all from previous projects so its all come together with a few days work.
19 January 2010
Subsumption Architecture - Introduction
- I simply don't recall the reason so have to remember it or rethink it. That is why you should document things.
- There was a valid reason that is no longer valid. Boy, that happens all the time in development. A good habit to develop is to revist assumpts regularly to see how they've changed.
- I simply blew it when writing the code many months ago.
- ...or some totally different situation that I can't think of right now.
That is the basics of subsumption, though. A good book on robot programming that covers subsumption is Robot Programming - A Practical Guide to Behavior-Based Robotics" by Joseph L. Jones.
...sine die
03 September 2009
ADC2 - Package Name
One of the last minute requirements for submission to ADC2 was that the Java package name be different from the name used in the Android Market. My package originally was com.mysticlake.galacticguardian.
As you know I'm not a Java guru so I wondered what I could do that would be easy. I finally changed the 'com' to 'adc2'. Eclipse readily made the change and all was good.
Except for version control. I use Subversion. The repository lives on my laptop computer. (Then I always have everything with me.) Subversion did not care for changing the top directory name. For all my years of development experience I've not worked with version control systems except in the most rudimentary fashion. Guess I need to figure out branching and merging.
Game Submitted to ADC2 - Galactic Guardian: Zap GPS
It was a bit of a crunch because a week ago my throat started feeling gluncky and by Wed I knew I was not well. (Or at least less well than usual.) Today is the first day I feel okay. But I persevered. My wife, Shari was really supportive taking over the cooking and a couple other household tasks I usually perform. I am retired but she still works as a college professor. Fortunately, it was the first week of classes so she didn't have much grading or other work to do. Not that cooking was a big deal because all I wanted were BLTs and cereal most of the time. We did manage a Crosby, Stills and Nash concert last Friday evening which was a good time away from everything.
You can see the material that appears in the game Info at Mystic Lake Software. I need to do more with the web site but wanted to get that page up quickly. I'll be using the web page for describing the game for users and, possibly, some of my thinking about the game itself. In this blog I'll discuss the technical development. There is bound to be some overlap but I'll keep that to a minimum.
The game came together pretty well. I went to the doctor Thursday morning, annual physical scheduled a few weeks ago, and took the Android with me. While waiting in the examining room I played the game and found it more interesting than I had realized. Of course all the time I had played it previously I was watching more for problems or if something I'd just implemented worked. That was the first time I'd just played with it.
The weekend before my granddaughter Meg had her 10th birthday. I showed her the game as it was then and she found it interesting. She went over to my mother and described it to her quite excitedly. That was encouraging.
I doubt that the game will have enough pizazz for the developers challenge but you never know. If I didn't enter it I surely couldn't win so may as well try. Plus it gave me a deadline to work toward and use as an explanation to others about why I was busy.
Next task is to make some minor changes and submit it to the Android market so it will be direclty accessible to all, not just those who will judge. Then on to making some improvements...and maybe make it a paid for application.
20 July 2009
Sensor - Accelerometer & Magnetics II
I'm not greatly experienced in Java having mainly worked with C++ for the last 15 plus years. I did have a company provided seminar on Java and have a half-dozen Java books on the shelf. But I'm still on the learning curve.
In the code I obtained the magnetice, acclerometer, and orientation sensor arrays by simply assigning their values to another array. I then used that array in subsequent calls to onSensorChanged. Not the proper way of doing this. I should have cloned the arrays which makes a copy. For example:
case Sensor.TYPE_MAGNETIC_FIELD:
mags = s_ev.values;
isReady = true;
break;
should be:
case Sensor.TYPE_MAGNETIC_FIELD:
mags = s_ev.values.clone();
isReady = true;
break;
I found this when I output all the values for the sensors to LogCat each time any one of them was updated. I noticed that the values for the accelerometer changed from its previous value when the orientation sensor was changed.
13 July 2009
Sensor - Accelerometer & Magnetics
http://groups.google.com/group/android-developers/browse_frm/thread/1b42c48ce47cb1c9/720c6f4f8a40fc67#720c6f4f8a40fc67
http://groups.google.com/group/android-developers/browse_frm/thread/2e14272d72b7ab4f#
I had the basic code working so dug a little deeper into the rotation routines and the timing. I posted responses on the threads but want here to dig into the details more.
First some observations applicable to my G1:
- The sensors report approximetly every 20, 40 and 220 msec for FAST, GAME, and NORMAL.
- A sample may be missed for a specific sensor but usually one of them will be generated - but sometimes all can be missed.
- The magnetic field sensor is most reliable with only a few drops. The other sensors are dropped considerably more often.
A caveat in all this is the way I setup the sensor handling may make a difference. I have a single routine for onSensorChanged which handles all three sensors. It is possible that having three separate routines may produce different results.
One of the messages in the threads mentioned writing data to a file. I was concerned that writing to a file might cause delays in responding to the sensors. I collected my data by writing to the Log Cat. I then did a cut and paste to an editor, formatted the columns to CSV, and loaded the results into a spreadsheet for analysis.
Here is the code for capturing sensor information and peforming the rotations.
// ================================================================================================================
private class OrientationListner implements SensorEventListener {
final int matrix_size = 16;
float[] R = new float[matrix_size];
float[] outR = new float[matrix_size];
float[] I = new float[matrix_size];
float[] values = new float[3];
boolean isReady = false;
DigitalFilter[] filter =
{ new DigitalFilter(), new DigitalFilter(), new DigitalFilter(), new DigitalFilter(),
new DigitalFilter(), new DigitalFilter() };
private long lastMagsTime;
private long lastAccelsTime;
private long lastOrientsTime;
// ------------------------------------------------------------------------------------------------------------
public void onSensorChanged(SensorEvent s_ev) {
Sensor sensor = s_ev.sensor;
int type = sensor.getType();
switch (type) {
case Sensor.TYPE_MAGNETIC_FIELD:
mags = s_ev.values;
isReady = true;
break;
case Sensor.TYPE_ACCELEROMETER:
accels = s_ev.values;
break;
case Sensor.TYPE_ORIENTATION:
orients = s_ev.values;
Exp.mText04.setText("" + (int) orients[0]);
Exp.mText05.setText("" + (int) orients[1]);
Exp.mText06.setText("" + (int) orients[2]);
break;
}
if (mags != null && accels != null && isReady) {
isReady = false;
SensorManager.getRotationMatrix(R, I, accels, mags);
SensorManager.remapCoordinateSystem(R, SensorManager.AXIS_X, SensorManager.AXIS_Z, outR);
SensorManager.getOrientation(outR, values);
int[] v = new int[3];
v[0] = filter[0].average(values[0] * 100);
v[1] = filter[1].average(values[1] * 100);
v[2] = filter[2].average(values[2] * 100);
Exp.mText01.setText("" + v[0]);
Exp.mText02.setText("" + v[1]);
Exp.mText03.setText("" + v[2]);
}
}
// ----------------------------------------------------------------------------------------------------------------
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Update
I had a couple of requests for the DigitalFilter class. It is below although it is called DigitalAverage. I took it from a later version of the code where I changed the name to better indicate its actual operation. I originally callsed it 'Filter' because I thought I might get more complex than an a simple average.
No, I'm not going to explain how to integrate the two pieces of code. That is left as an exercise for the reader.
// ================================================================================================================
private class DigitalAverage {
final int history_len = 4;
double[] mLocHistory = new double[history_len];
int mLocPos = 0;
// ------------------------------------------------------------------------------------------------------------
int average(double d) {
float avg = 0;
mLocHistory[mLocPos] = d;
mLocPos++;
if (mLocPos > mLocHistory.length - 1) {
mLocPos = 0;
}
for (double h : mLocHistory) {
avg += h;
}
avg /= mLocHistory.length;
return (int) avg;
}
}
08 July 2009
Differentiate Emulator from Device & Unique IDs
As a long time embedded systems developer this advice is absurd. There is all kinds of development work that can be accomplished without actual inputs, especially on a device with a GUI like the Android.
Additionally, it just isn't convenient reaching over to my G1 to see what it is doing and to manipulate it to check orientation changes, etc. Hitting ctrl-F11 to change orientation on the emulator is much easier.
I started wondering how to detect if the emulator or a device were running an application. This would allow skipping over the buggy Sensor Manager on the emulator to avoid the hang. Putting some conditional coding around sensor code might then allow development on the emulator.
My investigation found two sets of information to differentiate the device and emulator. This information would also allow differentiating among devices so has some use in all applications.
The first set of information is from the OS build. There are a number of fields available. All you need is the import and reading of the available fields. The comments after the code are from my emulator and the G1.
import android.os.Build;
// ...
Log.d(TAG, "config" + "\n " + Build.BOARD + "\n " + Build.BRAND + "\n" + Build.DEVICE + "\n" + Build.DISPLAY + "\n" + Build.FINGERPRINT + "\n" + Build.HOST + "\n" + Build.ID + "\n" + Build.MODEL + "\n" + Build.PRODUCT + "\n" + Build.TAGS + "\n" + Build.TIME + "\n" + Build.TYPE + "\n" + Build.USER);
// trout
// tmobile
// dream
// CRB43
// tmobile/kila/dream/trout:1.5/CRB43/148830:user/ota-rel-keys,release-keys
// undroid11.mtv.corp.google.com
// CRB43
// T-Mobile G1
// kila
// ota-rel-keys,test-keys
// 1242268990000
// user
// android-build
// unknown
// generic
// generic
// sdk-eng 1.5 CUPCAKE 148875 test-keys
// generic/sdk/generic/:1.5/CUPCAKE/148875:eng/test-keys
// e-honda.mtv.corp.google.com
// CUPCAKE
// sdk
// sdk
// test-keys
// 1242347389000
// eng
// android-build
The second differentiator is the Android ID which may be unique to each device. The emulator returns null while the device returns a string. Again, after the code is my results, although I did corrupt my G1's ID since with proper permissions the value can be changed.
Log.d(TAG, "and id " + Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID));
// G1: 200xxxxxd4cca5x
// Emulator: null
SRC2 - Explicit Steering - Wheel Speed
SRC2 Rover This fourth post about the qualifying round of the NASA Space Robotics Challenge - Phase 2 (SRC2) addresses t he speed of the ...
-
The brain of a robot is the software. The software has to take in the sensor data, interpret it, and generate commands to the actuators. On...
-
Just as I was finishing my first look at the accelerometer and magnetic field sensors a couple of threads cropped up on the Android Develope...
-
Another NASA Centennial Challenge began earlier this year. It will be the 3rd I've entered. I also entered the 2019 ARIAC competition...