Computer Vision Balance Bot
Keith Young
Posts: 569
in Propeller 1
I'm making a balancing robot that can navigate mazes. I currently have the computer vision system solving the mazes using OpenCV on a Raspberry Pi B+. I'm now trying to get the balancing bit to work...
For now I'm trying to do this with an ActivityBot with a counterweight at the front.
Here goes my first attempt at putting in code in the new forums...
Well that failed faster than I figured. Says my body is too long... I guess I'll try attaching it for now?
Here is the problem
After a while of running, the left wheel will stop while the right wheel keeps on spinning. What gives? I am setting both sides of drive_rampstep the same. In fact it's drive_rampstep(dl,dl) in order to test if I was crazy or if indeed somehow the bot was going bonkers.
Can anyone shed some light on how one wheel can be stopping while the other keeps on spinning, when both are set to the same speed?
I'm using SimpleIDE on Windows at the moment.
Thanks
PS after this is working I hope to share how I did it. For now I'm on a deadline so the Raspi install, OpenCV install, OpenCV code etc will have to wait a couple weeks.
For now I'm trying to do this with an ActivityBot with a counterweight at the front.
Here goes my first attempt at putting in code in the new forums...
Well that failed faster than I figured. Says my body is too long... I guess I'll try attaching it for now?
Here is the problem
After a while of running, the left wheel will stop while the right wheel keeps on spinning. What gives? I am setting both sides of drive_rampstep the same. In fact it's drive_rampstep(dl,dl) in order to test if I was crazy or if indeed somehow the bot was going bonkers.
Can anyone shed some light on how one wheel can be stopping while the other keeps on spinning, when both are set to the same speed?
I'm using SimpleIDE on Windows at the moment.
Thanks
PS after this is working I hope to share how I did it. For now I'm on a deadline so the Raspi install, OpenCV install, OpenCV code etc will have to wait a couple weeks.
Comments
It now balances but is very jittery. I'm going to start working on filtering the data and smoothing the response.
Once I have the robot balancing I'll start designing my power system for the robot. I should be receiving the actual camera I'll be using at which point I can test the realtime maze solving capabilities.
One motor does clearly go faster than the other so I'll need to use another gyroscope axis and try to correct for unwanted turning of the ActivityBot.
Hopefully I can get a decent chassis together and have it all wrapped up in time for Maker Faire Detroit. Otherwise I'll have to revert to my old simpler bots.
More to come.
Some of the information I've been using:
http://roboticssamy.blogspot.pt/2013/11/rs4-robot.html#comment-form
https://github.com/TKJElectronics/BalancingRobotArduino
http://robotics.ee.uwa.edu.au/theses/2003-Balance-Ooi.pdf
I know I could go for long pauses, which would give me a smaller percentage of error in estimating the loop time, but that would make the robot more unstable.
I'm using C in SimpleIDE.
I need the accurate loop time for a DT when estimating angle based on the gyroscope and accelerometer.
int loopTime = cnt; // Get the current system counter
while (true) // Repeat forever{ // Run some code here
loopTime += 80000000 / 400; // Run at 400Hz, assuming an 80MHz clock waitcnt( loopTime );}
(note that this is untested - the names of things may be incorrect - I usually code in Spin)
while(1)
target_cnt = cnt + clkfreq/20
calculate a bunch of stuff
while(cnt < target_cnt)
pause(1)
Thanks Jason, you're a genius!
and the Dancebot:http://onerobot.org/products/dancebot/
He wrote a whole chapter on the dancebot in this book:
https://www.parallax.com/product/32316
Right now I'm hung up on getting halfway decent angle measurements quickly despite the Gyro and Accelerometer seeming to only put out integers. By the time I have a full degree of error it takes a fair bit of correction, let alone that by the time it's measured it might be 2 degrees with the addition of some angular velocity.
I could try a Kalman Filter but the robot is very sensative to the speed of the loop even with such a simple estimating algorithm I'm using (similar to complementary filter).
pause(1)
waitcnt() does exactly that, but the cog will use less power while in waitcnt state, but it takes zero clocks for it come out of that state, so by using waitcnt, you actually get more precise timing, and it uses less power.
Is in c++ on the Pi
Is in c for the Prop (SimpleIDE)
Prefer using USB rather than GPIO, but will take what I can get.
The robot can now solve the mazes through computer vision and I need to start sending drive commands to the Prop which is on an ActivityBot.
After Maker Faire Detroit this weekend I intend to get everything I have on here to make it easier for anyone else trying to make a similar robot in the future. Hopefully we can build on eachothers efforts and make some pretty awesome Pi/Prop Bots.
Thanks for all the help so far.
through either the serial port or a USB port. I use the WiringPi library for C++ serial on the Pi.
What did you do with your robot out of curiosity?
of OpenCV you are using. With that information I can post the command line to compile it with.
Edit: It doesn't solve mazes but it track's color objects and moves a servo pan/tilt camera platform to track the
object.
the -lopencv_videoio if it gives you a problem. Also you can find the WiringPi library here -
http://wiringpi.com/
Here is the command line I use to compile -
g++ -Wall -o lgj lgj.cpp -lwiringPi -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_videoio
Edit: I am having a problem attaching the file. I can post the code if someone tells me the magic code blocks to use at the start and end.
Edit: This file is called lgj.cpp
Edit2: There is no error checking in this. You will have to make sure that you have a /dev/video0 and a /dev/ttyUSB0 before running the program.
#include <sstream> /* track largest red object and send serial screen location/area */
#include <opencv2/highgui/highgui.hpp> /* video A = live video plus text info and tracking square, video B = binary masked objects */
#include <opencv2/imgproc/imgproc.hpp>
#include <wiringSerial.h>
using namespace cv;
using namespace std;
int main() {
VideoCapture capture(0);
// int fd(serialOpen("/dev/ttyAMA0", 115200));
int fd(serialOpen("/dev/ttyUSB0", 115200));
int big, fps;
Mat image, imgCopy, hsv, thresh, threshCopy;
stringstream ss;
namedWindow("A");
namedWindow("B");
moveWindow("A", 0, 100);
moveWindow("B", 500, 100);
while(1) {
int t = (int)getTickCount();
capture >> image;
resize(image, image, Size(200, 140));
image.copyTo(imgCopy);
blur(image, image, Size(5, 5));
cvtColor(image, hsv, CV_BGR2HSV);
inRange(hsv, Scalar(0, 35, 35), Scalar(3, 255, 255), thresh);
thresh.copyTo(threshCopy);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(thresh, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<Moments> mu(contours.size());
int maxArea = 0;
for (unsigned int cnt(0); cnt < contours.size(); cnt++) {
int area = contourArea(contours[cnt]);
mu[cnt] = moments(contours[cnt], false);
if (area > maxArea) {
maxArea = area;
big = cnt;
}
}
if (maxArea > 0) {
int x = mu[big].m10 / mu[big].m00;
int y = mu[big].m01 / mu[big].m00;
rectangle(imgCopy, Point(x - 5, y - 5), Point(x + 5, y + 5), Scalar(255, 255, 255), 2, 8, 0);
ss.str("");
ss << "FPS " << fps;
putText(imgCopy, ss.str(), Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 255), 2, 8, 0);
ss.str("");
ss << "X " << x;
putText(imgCopy, ss.str(), Point(0, 35), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 255), 2, 8, 0);
ss.str("");
ss << "Y " << y;
putText(imgCopy, ss.str(), Point(0, 55), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 255), 2, 8, 0);
ss.str("");
ss << "A " << maxArea;
putText(imgCopy, ss.str(), Point(0, 75), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 255), 2, 8, 0);
serialPuts(fd, "!~");
serialPutchar(fd, x);
serialPutchar(fd, y);
serialPutchar(fd, maxArea & 0xff);
serialPutchar(fd, maxArea >> 8);
}
imshow("A", imgCopy);
imshow("B", threshCopy);
if (waitKey(1) == 27) { /* escape key exits program */
serialClose(fd);
capture.release();
destroyAllWindows();
break;
}
fps = getTickFrequency() / ((int)getTickCount() - t);
}
}
code I posted here. It is with the last serialPutchar instruction. It should look like this -
serialPutchar(fd, maxArea >> 8)
Edit:OK it is maxArea shifted right 8 bits.
http://forums.parallax.com/discussion/145332/raspberry-pi-and-propellor
The Pi compiles and the old software runs fine. It detects the Prop. Now I'm trying to get the Prop to read values sent from the Pi.
The goal is to say something like (Turn or Straight) (Degrees or Distance).
because of all of the access it gives to the Pi's GPIO stuff. Good luck and have fun with your project!
I can currently light up the rx light on the ActivityBoard. I just can't currently get my int to change apparently.
problem with? Maybe someone can give you some help.
Currently the serial portion of my Pi code looks like this:
#include <termios.h>
#include <sys/fcntl.h>
struct termios termOptions;
char port[1024];
int ttyFid;
ttyFid = open( "/dev/ttyUSB0", O_RDWR );
tcgetattr( ttyFid, &termOptions );
cfsetispeed( &termOptions, B115200);
cfsetospeed( &termOptions, B115200);
tcsetattr( ttyFid, TCSANOW, &termOptions );
char ch;
ch = 10;
while(1)
{
write( ttyFid, &ch, sizeof(ch) );
}
My prop is just blinking an LED on a loop trying to rx. If it gets the rx right it should blink the light much slower.
The code is currently returning a -1 at tcgetattr( ttyFid, &termOptions ) but nothing blows up. I still get the red rx light on the ActivityBoard.
http://forums.parallax.com/discussion/145332/raspberry-pi-and-propellor/p1
#include "simpletools.h"
#include "abdrive.h"
#include "fdserial.h"
fdserial *comms;
//#include <math.h>
int acc = 1;
int main()
{
comms = fdserial_open( 30, 31, 0, 115200);
while(1)
{
fdserial_rxTime( acc, 100 );
//readChar(31, acc);
high(27);
pause(acc*125);
low(27);
pause(acc*125);
if( acc == -1 ) high(26);
}
}
constantly to the Propeller. I made some changes to the Pi code to get it to compile.
#include <unistd.h>
#include <termios.h>
#include <sys/fcntl.h>
int main() {
struct termios termOptions;
char port[1024];
int ttyFid;
ttyFid = open("/dev/ttyUSB0", O_RDWR);
tcgetattr(ttyFid, &termOptions);
cfsetispeed(&termOptions, B115200);
cfsetospeed(&termOptions, B115200);
tcsetattr(ttyFid, TCSANOW, &termOptions);
char ch;
ch = 10;
while(1) {
write(ttyFid, &ch, sizeof(ch));
}
}
I don't really see the change besides the new include, some excluded includes, and adding int main. Am I missing something? I do have an int main etc, I just have ~300 lines unrelated to serial and therefore only included the parts related to serial.
warning about the unused variable 'port'. Probably because you are not receiving anything. But it does open
/dev/ttyUSB0 and outputs the character.
I've also tried:
write( ttyFid, "%d", ch );
Nothing seems to work. My light won't blink slower. The rx light on the ActivityBoard does blip red however.