Some people play tennis, some try to build autonomous wheeled robots. I do the later and the result has come to be known as “Terry”. In the long road toward that goal what started as a direct drive, “give the motor a PWM of X% of the total power”, lead to having encoder feedback so that the wheel could be turned an exact amount over a given time. This has meant that the control interface no longer uses a direct power and direction slider but has buttons to perform specific motion tasks. The button with the road icon and a 1 will move the wheels forward a single rotation to advance Terry 6π inches forwards (6 inch wheels).
The red tinted buttons use the wheel encoders to provide precision movement. Enc left and Enc right turn Terry in place (one wheel forward, one wheel backwards). The Circus and Oval perform patrol like maneuvers. Circus moves forward, turns in place, returns back, and turns around again. It's like a strafing patrol. On the other hand, the Oval turns move like a regular car, with both wheels going forward but one wheel moving slower than the other to create the turning effect. The pan and tilt control the on board camera for a good Terry eye view of the world.
I added two way web socket communications to Terry this week. So now the main battery voltage is updated and the current, err, current for each motor is shown at the bottom of the page. The two white boxes there give version information so you can see if the data is stale or not. I suspect a little async javascript on some model value will be coming so that items on the page will darken as they become stale due to lag on Terry or communications loss.
I've been researching proximity detection and will be integrating a Kinect for longer distance measures soon. Having good reliable distance measurements from Terry to objects is the first step in getting him to create maps of the environment for autonomous navigation.
C++, Linux, libferris and embedded development. Yet another blog from yet another NARG.
Showing posts with label bbb. Show all posts
Showing posts with label bbb. Show all posts
Tuesday, July 1, 2014
Monday, May 26, 2014
To beep or not to beep.
There comes a time in the life of any semi autonomous robot when the study of the classics occurs. For Terry, the BeagleBone Black driven Actobotics construction, short of visiting the Globe the answer would seem to be at current "To Beep".
Terry is now run by a RoboClaw 2x5A controller board with two 1024 pulse per revolution shaft encoders attached to an 8:1 ratio gearing on the wheel. This gives an overall of about 13 bits of precision from the shaft encoders relative to the wheel. In the future I can use this magnificent 4 inch alloy gear wheel as a torque multiplier by moving the motor to its own tooth pinion gear. Terry now also has an IR distance sensor out front, and given the 512mb of RAM on the BeagleBone some fairly interesting mapping can be built up by just driving around and storing readings. Distance + accurate shaft encoders for the win?
To use the RoboClaw from the BeagleBone Black I created a little nodejs class. It's not fully complete yet, but it is already somewhat useful. I found creating the nodejs interesting because when coding for MCUs one gets quite used to the low level style of sending bytes and stalling until the reply comes in. For nodejs such a style doesn't implement well. Normally one would see something like
doMagic( 7, rabbits, function() {
console.log("got them rabbitses, we did");
});
Where the callback function is performed when the task of pulling the number of rabbits from the hat is complete. For using the RoboClaw you might have something like
claw.getVersion( function( v ) {
console.log("version:" + v.value );
});
claw.setQPPS( 1024 );
Which all seems fairly innocent. The call to getVersion will send the VERSION command to the roboclaw with a given address (you can have many claws on the same bus). The claw should then give back a version string and a checksum byte. The checksum can be stripped off and verified by the nodejs.
The trouble is that the second call, to set of the quadratic decoder pulses per second, will start to happen before the RoboClaw got a chance to service the getVersion call. Trying to write a second command before you have the full reply from the first is a recipe for disaster. So the nodejs RoboClaw class maintains a queue of requests. The setQPPS() is not run right away, but enqueued for later. Once the bytes come back over the UART in response to the getVersion() then the callback is run and the RoboClaw nodejs class then picks the next command (setQPPS) and sends that to the RoboClaw. This way you get the strict ordered serial IO that the RoboClaw hardware is needing, but the nodejs can execute many commands without any stalling. Each command is handled in the order it is written in the nodejs source code, it just doesn't execute right away.
Next up is trying to tune the PID controller variables for the robot. One might expect a command such as "move 1 wheel circumference forward" to work fairly exactly if the quality of the shaft encoders is good. Though for the wheel size and QPPS I get a little bit of overshoot currently. I suspect the current D is not sufficient to pull it up in time. It may be a non issue when the wheels are down and friction helps the slow down process.
Terry now also has an IMU onboard. I created a small class to read from the TWI HMC5883 magneto. You'll have to figure your declination and I have a small hack on about line 100 to alter the heading so that a 0 degree reading means that physically Terry is facing North. I should bring that parameter out of the class so that its easier to adjust for other robots that might mount their IMU in a different physically orientation than what I happened to use.
Terry is now run by a RoboClaw 2x5A controller board with two 1024 pulse per revolution shaft encoders attached to an 8:1 ratio gearing on the wheel. This gives an overall of about 13 bits of precision from the shaft encoders relative to the wheel. In the future I can use this magnificent 4 inch alloy gear wheel as a torque multiplier by moving the motor to its own tooth pinion gear. Terry now also has an IR distance sensor out front, and given the 512mb of RAM on the BeagleBone some fairly interesting mapping can be built up by just driving around and storing readings. Distance + accurate shaft encoders for the win?
To use the RoboClaw from the BeagleBone Black I created a little nodejs class. It's not fully complete yet, but it is already somewhat useful. I found creating the nodejs interesting because when coding for MCUs one gets quite used to the low level style of sending bytes and stalling until the reply comes in. For nodejs such a style doesn't implement well. Normally one would see something like
doMagic( 7, rabbits, function() {
console.log("got them rabbitses, we did");
});
Where the callback function is performed when the task of pulling the number of rabbits from the hat is complete. For using the RoboClaw you might have something like
claw.getVersion( function( v ) {
console.log("version:" + v.value );
});
claw.setQPPS( 1024 );
Which all seems fairly innocent. The call to getVersion will send the VERSION command to the roboclaw with a given address (you can have many claws on the same bus). The claw should then give back a version string and a checksum byte. The checksum can be stripped off and verified by the nodejs.
The trouble is that the second call, to set of the quadratic decoder pulses per second, will start to happen before the RoboClaw got a chance to service the getVersion call. Trying to write a second command before you have the full reply from the first is a recipe for disaster. So the nodejs RoboClaw class maintains a queue of requests. The setQPPS() is not run right away, but enqueued for later. Once the bytes come back over the UART in response to the getVersion() then the callback is run and the RoboClaw nodejs class then picks the next command (setQPPS) and sends that to the RoboClaw. This way you get the strict ordered serial IO that the RoboClaw hardware is needing, but the nodejs can execute many commands without any stalling. Each command is handled in the order it is written in the nodejs source code, it just doesn't execute right away.
Next up is trying to tune the PID controller variables for the robot. One might expect a command such as "move 1 wheel circumference forward" to work fairly exactly if the quality of the shaft encoders is good. Though for the wheel size and QPPS I get a little bit of overshoot currently. I suspect the current D is not sufficient to pull it up in time. It may be a non issue when the wheels are down and friction helps the slow down process.
Terry now also has an IMU onboard. I created a small class to read from the TWI HMC5883 magneto. You'll have to figure your declination and I have a small hack on about line 100 to alter the heading so that a 0 degree reading means that physically Terry is facing North. I should bring that parameter out of the class so that its easier to adjust for other robots that might mount their IMU in a different physically orientation than what I happened to use.
Labels:
actobotics,
bbb,
beaglebone black,
bonescript,
nodejs,
roboclaw,
robotics,
servocity,
sparkfun
Saturday, May 10, 2014
Going from Arduino TWI to BeagleBone Black Bonescript
It's handy for a robot to know what orientation it is holding, and for that a Magnetometer can give you where north is at relative to the chip orientation. For this I used the HMC5883. I started out with a cut down minimal version of code running on Arduino and then started porting that over to Bonescript for the BeagleBone Black to execute.
The HMC5883 only needs power (3v3), ground, and the two wires of a TWI.
One of the larger API changes of the port is the Arduino line:
Wire.requestFrom( HMC5883_ADDRESS_MAG, 6 );
Which takes the TWI address of the chip you want 6 bytes from. It has to be prefixed by another TWI transmission to set the read byte address to start from where you would like, in this case HMC5883_REGISTER_MAG_OUT_X_H_M = 0x03.
On the Bonescript side this becomes the following. The TWI address isn't used because the wire object already knows that, but you pass the register address to start reading from directly to the request to read bytes:
The HMC5883 only needs power (3v3), ground, and the two wires of a TWI.
One of the larger API changes of the port is the Arduino line:
Wire.requestFrom( HMC5883_ADDRESS_MAG, 6 );
Which takes the TWI address of the chip you want 6 bytes from. It has to be prefixed by another TWI transmission to set the read byte address to start from where you would like, in this case HMC5883_REGISTER_MAG_OUT_X_H_M = 0x03.
On the Bonescript side this becomes the following. The TWI address isn't used because the wire object already knows that, but you pass the register address to start reading from directly to the request to read bytes:
self.wire.readBytes( HMC5883_REGISTER_MAG_OUT_X_H_M,
BUFFER_SIZE,
function(err, res) { ... });
I pushed the code up to github bbbMagnetometerHMC5883. Note that getting "north" requires both a proper declination angle for your location and also some modification to allow for how your magneto chip is physically oriented on your robot. Actual use of the chip is really easy with the async style of nodejs.
I pushed the code up to github bbbMagnetometerHMC5883. Note that getting "north" requires both a proper declination angle for your location and also some modification to allow for how your magneto chip is physically oriented on your robot. Actual use of the chip is really easy with the async style of nodejs.
Saturday, November 9, 2013
RePaper 2.7 inch epaper goodness from the BeagleBone
A little while back I bought a rePaper 2.7 inch eInk display. While the smaller, down to 1.4 inch screens have few enough pixels to be driven from an Arduino, the 264x176 screen should need around 5.5k for a single frame buffer, and you need two buffers to "wax on, wax off" the image on the display in order to update. The short story is that these displays work nicely from the BeagleBone Black. You have to have a fairly recent kernel in order to get the right sys files for the driver. Hint: if you have no "duty" file for your pwm then you have too old of a kernel.
So the first image I chose to display after the epd_test was a capture of fontforge editing Cantarell Regular. Luckily, I've made no changes to the splineset so my design skills are not part of the image. The rendering of splines in the charview of fontforge uses antialiasing, as it was switched over to cairo around a year ago. As the eInk display is monochrome the image displayed is dithered back to 1 bit.
With the real time collaboration support in fontforge this does raise the new chance to see a font being rendered on eInk as you design it (or hint it). I'm not sure how many fonts are being designed with eInk as the specific consumption ground. If you are interested in font design, checkout Crafting Type which uses fontforge to create new type, and you should also be able to see the collaboration and HTML preview modes in action.
Getting the actual eInk display to go from the BeagleBone had a few steps. Firstly, I managed to completely fill up the 2gb of eMMC where my Angstrom was installed. So now I'm running the whole show off a high speed 8gb sandisk card. I spent a little extra cash on a faster card, its one of the extreme super panda + extra adjective sandisk ones. The older kernel I had didn't have a duty file for the PWM pin that the driver wanted to use. Now I that I have a fully updated beaglebone black boot area I have that file. FWIW I'm on kernel version 3.8.13-r23a.49.
Trying out the epd_test initially showed me some broken lines and after a little bit what looked like a bit of the cat from the test image. After rechecking the wireup a few times I looked at the code and saw it was expecting a 2 inch screen. That happens in a few places in the code. So I changed those to reflect my hardware. Then the test loop ran as expected!
The next step was getting the FUSE driver installed (change for size needed too). Then the python demos could run. And thus the photo above was made. My next step is to create a function to render cairo to /dev/epd/display in order to drive the display directly from a cairo app.
A huge thank you to rePaper for making this so simple to get going. The drivers for Raspberry and Beagle are up on their github page. I had been looking at the Arduino driver and it's SPI code thinking about porting that over to Linux, but now that's not necessary! I might design some cape love for this, perhaps with a 14 pin IDC connector on it for eInk attaching. Shouldn't look much worse than last night's SPI only monster, though something etched would be nicer.
The 2.7 inch changes are below, the first one is just slightly more verbose error reporting. You'll also want to set EPD_SIZE=2.7 in /etc/init.d/epd-fuse.
diff --git a/PlatformWithOS/BeagleBone/gpio.c b/PlatformWithOS/BeagleBone/gpio.c
index b3ded6f..d1df3df 100644
--- a/PlatformWithOS/BeagleBone/gpio.c
+++ b/PlatformWithOS/BeagleBone/gpio.c
@@ -767,7 +767,7 @@ static bool PWM_enable(int channel, const char *pin_name) {
usleep(10000);
}
if (pwm[channel].fd < 0) {
- fprintf(stderr, "PWM failed to appear\n"); fflush(stderr);
+ fprintf(stderr, "PWM failed to appear pin:%s file:%s\n", pin_name, pwm[channel]
free(pwm[channel].name);
pwm[channel].name = NULL;
break; // failed
diff --git a/PlatformWithOS/demo/EPD.py b/PlatformWithOS/demo/EPD.py
index da1ef12..41cc6c1 100644
--- a/PlatformWithOS/demo/EPD.py
+++ b/PlatformWithOS/demo/EPD.py
@@ -48,8 +48,8 @@ to use:
def __init__(self, *args, **kwargs):
self._epd_path = '/dev/epd'
- self._width = 200
- self._height = 96
+ self._width = 264
+ self._height = 176
self._panel = 'EPD 2.0'
self._auto = False
diff --git a/PlatformWithOS/driver-common/epd_test.c b/PlatformWithOS/driver-common/epd_test.c
index e2f2b5a..afe3cb8 100644
--- a/PlatformWithOS/driver-common/epd_test.c
+++ b/PlatformWithOS/driver-common/epd_test.c
@@ -72,7 +72,7 @@ int main(int argc, char *argv[]) {
GPIO_mode(reset_pin, GPIO_OUTPUT);
GPIO_mode(busy_pin, GPIO_INPUT);
- EPD_type *epd = EPD_create(EPD_2_0,
+ EPD_type *epd = EPD_create(EPD_2_7,
panel_on_pin,
border_pin,
discharge_pin,
So the first image I chose to display after the epd_test was a capture of fontforge editing Cantarell Regular. Luckily, I've made no changes to the splineset so my design skills are not part of the image. The rendering of splines in the charview of fontforge uses antialiasing, as it was switched over to cairo around a year ago. As the eInk display is monochrome the image displayed is dithered back to 1 bit.
With the real time collaboration support in fontforge this does raise the new chance to see a font being rendered on eInk as you design it (or hint it). I'm not sure how many fonts are being designed with eInk as the specific consumption ground. If you are interested in font design, checkout Crafting Type which uses fontforge to create new type, and you should also be able to see the collaboration and HTML preview modes in action.
Getting the actual eInk display to go from the BeagleBone had a few steps. Firstly, I managed to completely fill up the 2gb of eMMC where my Angstrom was installed. So now I'm running the whole show off a high speed 8gb sandisk card. I spent a little extra cash on a faster card, its one of the extreme super panda + extra adjective sandisk ones. The older kernel I had didn't have a duty file for the PWM pin that the driver wanted to use. Now I that I have a fully updated beaglebone black boot area I have that file. FWIW I'm on kernel version 3.8.13-r23a.49.
Trying out the epd_test initially showed me some broken lines and after a little bit what looked like a bit of the cat from the test image. After rechecking the wireup a few times I looked at the code and saw it was expecting a 2 inch screen. That happens in a few places in the code. So I changed those to reflect my hardware. Then the test loop ran as expected!
The next step was getting the FUSE driver installed (change for size needed too). Then the python demos could run. And thus the photo above was made. My next step is to create a function to render cairo to /dev/epd/display in order to drive the display directly from a cairo app.
A huge thank you to rePaper for making this so simple to get going. The drivers for Raspberry and Beagle are up on their github page. I had been looking at the Arduino driver and it's SPI code thinking about porting that over to Linux, but now that's not necessary! I might design some cape love for this, perhaps with a 14 pin IDC connector on it for eInk attaching. Shouldn't look much worse than last night's SPI only monster, though something etched would be nicer.
The 2.7 inch changes are below, the first one is just slightly more verbose error reporting. You'll also want to set EPD_SIZE=2.7 in /etc/init.d/epd-fuse.
diff --git a/PlatformWithOS/BeagleBone/gpio.c b/PlatformWithOS/BeagleBone/gpio.c
index b3ded6f..d1df3df 100644
--- a/PlatformWithOS/BeagleBone/gpio.c
+++ b/PlatformWithOS/BeagleBone/gpio.c
@@ -767,7 +767,7 @@ static bool PWM_enable(int channel, const char *pin_name) {
usleep(10000);
}
if (pwm[channel].fd < 0) {
- fprintf(stderr, "PWM failed to appear\n"); fflush(stderr);
+ fprintf(stderr, "PWM failed to appear pin:%s file:%s\n", pin_name, pwm[channel]
free(pwm[channel].name);
pwm[channel].name = NULL;
break; // failed
diff --git a/PlatformWithOS/demo/EPD.py b/PlatformWithOS/demo/EPD.py
index da1ef12..41cc6c1 100644
--- a/PlatformWithOS/demo/EPD.py
+++ b/PlatformWithOS/demo/EPD.py
@@ -48,8 +48,8 @@ to use:
def __init__(self, *args, **kwargs):
self._epd_path = '/dev/epd'
- self._width = 200
- self._height = 96
+ self._width = 264
+ self._height = 176
self._panel = 'EPD 2.0'
self._auto = False
diff --git a/PlatformWithOS/driver-common/epd_test.c b/PlatformWithOS/driver-common/epd_test.c
index e2f2b5a..afe3cb8 100644
--- a/PlatformWithOS/driver-common/epd_test.c
+++ b/PlatformWithOS/driver-common/epd_test.c
@@ -72,7 +72,7 @@ int main(int argc, char *argv[]) {
GPIO_mode(reset_pin, GPIO_OUTPUT);
GPIO_mode(busy_pin, GPIO_INPUT);
- EPD_type *epd = EPD_create(EPD_2_0,
+ EPD_type *epd = EPD_create(EPD_2_7,
panel_on_pin,
border_pin,
discharge_pin,
Friday, June 7, 2013
BeagleBone Black: Walking the dog.
My software guy with a soldering iron fun has recently extended to the BeagleBone Black. This is a wonderful little ARM machine with a 1Ghz CPU, a whole bunch of GPIO pins, I2C, SPI, AIN.. all the fun things packed into a $45 board.
On an unrelated purchase, I got a small 1.8 inch TFT display that can do 128x160 with a bunch of colours using the st7735 chip. That's shown above running the qtdemo on the framebuffer. Of course, an animation might serve to better show that off. The display was on sale for $10 and so it was then on it's way to me :) My original plan was to drive that from an Arduino... Looking around I noticed that Matt Porter had generously contributed a driver to run the st7735 over SPI from the Linux kernel. The video of him talking at ELC about this framebuffer driver was also very informative :) It seems the same TFT can be run from the Raspberry or Beagle series of hardware.
The wiring for the panel I got was a bit different than the adafruit one that Matt used. But once you have the pinouts its not so hard to figure out. I've currently left the 5V rail unconnected on my TFT. On the BeagleBone Black the HDMI output captures a whole bunch of pins when it starts. Unfortunately some of those pins are needed for the little TFT. One might be able to reroute the SPI to the other bus or mux the pins differently to get around that and have HDMI and the TFT at once. But I wanted to get the TFT going to see if/how it worked before changing the pins.
I had found some info on putting a line in eEnv.txt to stop the HDMI cape from loading but that didn't work for me. On my board I saw that in /sys/devices/bone_capemgr.9/slots the HDMI was the 5th cape. When I first echoed "-5" into the slots file to unload that cape the kernel gave a backtrace. If I did the same on a freshly booted bone it would cleanly remove the HDMI cape though. So something was using the HDMI cape driver before which didn't want to be removed.
With the HDMI cape unloaded the next step is to load a "firmware" file that reserves the pins that the st7735fb driver wants to use. Since I used the same pins on the bone as the adafruit display wants I could just use the below.
echo cape-bone-adafruit-lcd-00A0 > /sys/devices/bone_capemgr.9/slots
A dmesg showed that a new framebuffer device fb0 had come into existence.
[ 85.280471] bone-capemgr bone_capemgr.9: slot #6: Requesting firmware 'cape-bone-adafru-00A0.dtbo' for board-name 'Override Board Name', version '00A0'
[ 85.284645] bone-capemgr bone_capemgr.9: slot #6: dtbo 'cape-bone-adafru-00A0.dtbo' loaded; converting to live tree
...
[ 86.235178] fb0: ST7735 frame buffer device,
[ 86.235178] using 40960 KiB of video memory
[ 86.236687] bone-capemgr bone_capemgr.9: slot #6: Applied #5 overlays.
After a bunch of searching around trying various things, I found that prescaling in mplayer can display to the framebuffer:
# mplayer -ao null -vo fbdev2:/dev/fb0 -x 128 -y 160 -zoom ArduSat_Open_Source_in_orbit.mp4
The qtdemo also runs "ok" by executing the below. I say ok because it obviously expects a higher resolution display than 128x160.... qtdemoE -qws
It is tempting to have two screens and add a touch sensitive film to them. With a QML/QtQuick/TodaysRebrand^TM interface the GUI should work well and be flickable to many screens.
A great hack I look forward to is running a 32x16 LED DMD using a deferred rendering framebuffer driver like the st7735fb does. I see the evil plan now, release the BeagleBone Black for $45 and draw more C/C++ programmers to being kernel hackers rather than userland ones :)
On an unrelated purchase, I got a small 1.8 inch TFT display that can do 128x160 with a bunch of colours using the st7735 chip. That's shown above running the qtdemo on the framebuffer. Of course, an animation might serve to better show that off. The display was on sale for $10 and so it was then on it's way to me :) My original plan was to drive that from an Arduino... Looking around I noticed that Matt Porter had generously contributed a driver to run the st7735 over SPI from the Linux kernel. The video of him talking at ELC about this framebuffer driver was also very informative :) It seems the same TFT can be run from the Raspberry or Beagle series of hardware.
The wiring for the panel I got was a bit different than the adafruit one that Matt used. But once you have the pinouts its not so hard to figure out. I've currently left the 5V rail unconnected on my TFT. On the BeagleBone Black the HDMI output captures a whole bunch of pins when it starts. Unfortunately some of those pins are needed for the little TFT. One might be able to reroute the SPI to the other bus or mux the pins differently to get around that and have HDMI and the TFT at once. But I wanted to get the TFT going to see if/how it worked before changing the pins.
I had found some info on putting a line in eEnv.txt to stop the HDMI cape from loading but that didn't work for me. On my board I saw that in /sys/devices/bone_capemgr.9/slots the HDMI was the 5th cape. When I first echoed "-5" into the slots file to unload that cape the kernel gave a backtrace. If I did the same on a freshly booted bone it would cleanly remove the HDMI cape though. So something was using the HDMI cape driver before which didn't want to be removed.
With the HDMI cape unloaded the next step is to load a "firmware" file that reserves the pins that the st7735fb driver wants to use. Since I used the same pins on the bone as the adafruit display wants I could just use the below.
echo cape-bone-adafruit-lcd-00A0 > /sys/devices/bone_capemgr.9/slots
A dmesg showed that a new framebuffer device fb0 had come into existence.
[ 85.280471] bone-capemgr bone_capemgr.9: slot #6: Requesting firmware 'cape-bone-adafru-00A0.dtbo' for board-name 'Override Board Name', version '00A0'
[ 85.284645] bone-capemgr bone_capemgr.9: slot #6: dtbo 'cape-bone-adafru-00A0.dtbo' loaded; converting to live tree
...
[ 86.235178] fb0: ST7735 frame buffer device,
[ 86.235178] using 40960 KiB of video memory
[ 86.236687] bone-capemgr bone_capemgr.9: slot #6: Applied #5 overlays.
After a bunch of searching around trying various things, I found that prescaling in mplayer can display to the framebuffer:
# mplayer -ao null -vo fbdev2:/dev/fb0 -x 128 -y 160 -zoom ArduSat_Open_Source_in_orbit.mp4
The qtdemo also runs "ok" by executing the below. I say ok because it obviously expects a higher resolution display than 128x160.... qtdemoE -qws
It is tempting to have two screens and add a touch sensitive film to them. With a QML/QtQuick/TodaysRebrand^TM interface the GUI should work well and be flickable to many screens.
A great hack I look forward to is running a 32x16 LED DMD using a deferred rendering framebuffer driver like the st7735fb does. I see the evil plan now, release the BeagleBone Black for $45 and draw more C/C++ programmers to being kernel hackers rather than userland ones :)
Labels:
bbb,
beaglebone,
deferred rendering.,
kde,
small-tft,
spi,
st7735
Subscribe to:
Posts (Atom)



