Archive for March, 2011
2011 Summer Online Training schedule available!
The Robotics Academy has just released the Summer Online Training schedule for 2011. Take a look below for the dates:
- ROBOTC for LEGO / TETRIX
- July 11th-15th, 2011 (Monday-Friday for 1 Week)
- 10:00am-12:00pm EST (7:00am-9:00am PST)
- Register by June 27th, 2011
- CLICK HERE TO REGISTER for ROBOTC for LEGO / TETRIX
- ROBOTC for VEX PIC and CORTEX
- August 8th-12th, 2011 (Monday-Friday for 1 Week)
- 3:30pm-5:30pm EST (12:30pm-2:30pm PST)
- Register by July 25th, 2011
- CLICK HERE TO REGISTER for ROBOTC for VEX PIC and CORTEX
For questions about payment, please email customerservice@robomatter.com.
For questions relating to training, please email training@rec.ri.cmu.edu, or you can comment on this post below.
I2C on the VEX Cortex
The VEX Cortex is a nice platform made by VEX Robotics. It is supported in two programming environments, one of which is ROBOTC. Much to my dismay, the master firmware does not support I2C, which is why ROBOTC does not support it. I don’t really like it when someone tells me I can’t do something, so I went ahead and remedied the situation.
I spent a few evenings writing and tinkering in ROBOTC to write my own bit-banged I2C implementation, which much to my surprise, worked very well. First I tested it with the Mindsensors Magic Wand (above left) and later also with the Holit Data Systems Motor MUX and Mindsensors NXT Servo Controller (above right). Jesse Flot from Robotics Academy was kind enough to send me some old VEX cables so I could splice two of them into an NXT cable for I2C. I will post a HOWTO for that at a later date.
As you can see in the right picture, I was already contemplating controlling the omniwheeled robot with the Motor MUX and so I did.
The robot is remote controlled via VEXnet over Wifi (which is a totally awesome feature which I wish the NXT had). The short video was taken at the RobotMC meeting of 19 March 2011, which happened to coincide with an information day for the technical university where we hold our meetings.
The coolest part about it is that my driver suite is almost completely transparently portable to the VEX Cortex platform once you switch out the NXT I2C subsystem functions for the Cortex specific ones. Some NXT dependencies do need to be removed and made more generic. I intend to work on that in the next few weeks. That would make a very wide range of new sensors available to the VEX Cortex platform.
Original article: [LINK]
NXT Robot: PID Line Follower
DiMastero is at it again…
This time he has created a robot that does some very fast line following.
Watch it in action
Hardware
This line follower is equipped with three sensors: one light (port 3), one magnetic field (port 2) and one IR link, the last of which are by HiTechnic. The light sensor is used for the robot’s main purpose: line following, while the magnetic field sensor detects whether the robot needs to pause or keep going. The IR Link doesn’t have any function; it’s just there to keep the whole thing symmetrical.



The robot moves using two independently moving motors connected to ports B and C. They form the follower’s back and sides. At the front, next to the light sensor is a caster wheel.

Programming
The robot was programmed in RobotC and runs on PID control.The motors’ built-in PID is off. When the code starts, it takes the black (line) and white (background) light values and averages them to get an offset. It then sets the bias of the HiTechnic magnetic field sensor to 0, while the magnet is in front of it. That way, the sensor’s value will change when the magnet is (re)moved.

Next, after a short wait, it starts driving around the NXT test paper, guided by the PID. It keeps on doing so until the magnet is removed, in which case it pauses the program and turns off the motors. Once the magnet is back in place, the robot keeps going, even if it’s been moved to somewhere else on the line. If the magnet stays away for too long (more than four seconds), the program shuts down.
You can download the latest version of the code on the downloads page or download version 2.1 (which was the latest version when this page was last updated) below.
Setup and Performance
To start the robot, place it above the middle of the black line you want to follow right after starting the program on the NXT. Then, when it bleeps, move the robot to the left of the line. Make sure you neither touch nor move the magnet.
After a split second, the robot will start to follow the line. To pause it, lift the “tail”, moving the magnet. To get it back on line, let go of the tail. To abort the entire program, hold the tail for four seconds, or until you see the light sensor turn off (it’s in active mode, so the LED will be on all the time when it’s operative).
The robot follows the black line pretty quickly and smoothly.
Cortex “View Mode” Program
In an earlier blog post, we introduced the VEX LCD and showed how to print custom messages and values to it. Being able to print messages and sensor values adds some awesome functionality to your robot… but why stop there, when there’s so much more potential?
Many NXT (and even RCX!) users are familiar with the “View Mode” on their microcontrollers, that allows them to specify what sensors are connected to which ports, and then view those values on the screen. Without a built-in screen, VEX users haven’t had access to this functionality… that is, until now! If you have the VEX LCD, the code in this post will allow you to specify a sensor, specify which port it’s on, and then watch the sensor values update in real-time.
Step 1: Download and run the sample program on your robot (see below).
Step 2: Select the type of sensor connected to your robot. The left and right buttons cycle through the options. The middle button selects the current sensor.

Step 3: Select the port the sensor is connected to. For sensors with more than one wire, choose the FIRST port the sensor is plugged into (remember, sensors with more than two wires must be plugged in on neighboring ports on the Cortex). There is also a “Back to Sensors” option that will take you back to the sensor option menu.

Step 4: View the sensor data! Pressing the center button will exit, and return you to the sensor selection menu.

The sample code can be copied into ROBOTC, or you can download a copy here. Note: This code has not been fully optimized, but should work fine as-is.
//Global Variable Declarations
const short leftButton = 1;
const short centerButton = 2;
const short rightButton = 4;
short sensorChoice;
short portChoice;
short count;
//Task and Function Prototypes
void waitForPress();
void waitForRelease();
void clearPorts();
task sensorChooser();
task digitalPortChooser();
task analogPortChooser();
void menuTracker();
task display();
void analogSensorSetup(tSensors sensorPort);
void digitalSensorSetup(tSensors sensorPort);
//Main------------------------------------------------------------
task main()
{
StartTask(sensorChooser);
while(true)
{
wait1Msec(50);
}
}
//----------------------------------------------------------------
//sensorChooser - allows you to choose which sensor you'd like to pick
task sensorChooser()
{
//Junk Cleanup from previous runs
clearLCDLine(0);
clearLCDLine(1);
clearPorts();
waitForRelease();
StopTask(display);
StopTask(digitalPortChooser);
StopTask(analogPortChooser);
hogCPU();
count = 0;
while(true)
{
switch(count){
case 0:
//Touch
displayLCDCenteredString(0, "Touch Sensor");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 0;
releaseCPU();
StartTask(digitalPortChooser);
}
else if(nLCDButtons == leftButton)
{
waitForRelease();
count = 6;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count++;
}
break;
case 1:
//Quadrature Encoder
displayLCDCenteredString(0, "Quad Encoder");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 1;
releaseCPU();
StartTask(digitalPortChooser);
}
else
menuTracker();
break;
case 2:
//Ultrasonic
displayLCDCenteredString(0, "Ultrasonic");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 2;
releaseCPU();
StartTask(digitalPortChooser);
}
else
menuTracker();
break;
case 3:
//Light
displayLCDCenteredString(0, "Light Sensor");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 3;
releaseCPU();
StartTask(analogPortChooser);
}
else
menuTracker();
break;
case 4:
//Line Tracker
displayLCDCenteredString(0, "Line Tracker");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 4;
releaseCPU();
StartTask(analogPortChooser);
}
else
menuTracker();
break;
case 5:
//Potentiometer
displayLCDCenteredString(0, "Potentiometer");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 5;
releaseCPU();
StartTask(analogPortChooser);
}
else
menuTracker();
break;
case 6:
//Accelerometer
displayLCDCenteredString(0, "Accelerometer");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
sensorChoice = 6;
releaseCPU();
StartTask(analogPortChooser);
}
else if(nLCDButtons == leftButton)
{
waitForRelease();
count--;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count = 0;
}
break;
}
}
}
//----------------------------------------------------------------
//digitalPortChooser - allows you to choose which port you'd like to pick
task digitalPortChooser()
{
StopTask(sensorChooser);
hogCPU();
count = 1; //Chooses the starting point
while(true)
{
switch(count){
case 0:
//Back Option
displayLCDCenteredString(0, "Back to Sensors");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
releaseCPU();
StartTask(sensorChooser);
}
else if(nLCDButtons == leftButton)
{
waitForRelease();
count = 12;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count++;
}
break;
case 1:
//Digital 1
displayLCDCenteredString(0, "DIGITAL 1");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 1;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 2:
//Digital 2
displayLCDCenteredString(0, "DIGITAL 2");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 2;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 3:
//Digital 3
displayLCDCenteredString(0, "DIGITAL 3");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 3;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 4:
//Digital 4
displayLCDCenteredString(0, "DIGITAL 4");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 4;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 5:
//Digital 5
displayLCDCenteredString(0, "DIGITAL 5");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 5;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 6:
//Digital 6
displayLCDCenteredString(0, "DIGITAL 6");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 6;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 7:
//Digital 7
displayLCDCenteredString(0, "DIGITAL 7");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 7;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 8:
//Digital 8
displayLCDCenteredString(0, "DIGITAL 8");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 8;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 9:
//Digital 9
displayLCDCenteredString(0, "DIGITAL 9");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 9;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 10:
//Digital 10
displayLCDCenteredString(0, "DIGITAL 10");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 10;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 11:
//Digital 11
displayLCDCenteredString(0, "DIGITAL 11");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 11;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 12:
//Digital 12
displayLCDCenteredString(0, "DIGITAL 12");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 12;
releaseCPU();
StartTask(display);
}
else if(nLCDButtons == leftButton)
{
waitForRelease();
count--;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count = 0;
}
break;
}
}
}
//----------------------------------------------------------------
//analogPortChooser - allows you to choose which port you'd like to pick
task analogPortChooser()
{
StopTask(sensorChooser);
hogCPU();
count = 1;//Chooses the starting point
while(true)
{
switch(count){
case 0:
//Back Option
displayLCDCenteredString(0, "Back to Sensors");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
releaseCPU();
StartTask(sensorChooser);
}
else if(nLCDButtons == leftButton)
{
waitForRelease();
count = 8;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count++;
}
break;
case 1:
//Analog 1
displayLCDCenteredString(0, "ANALOG 1");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 13;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 2:
//Analog 2
displayLCDCenteredString(0, "ANALOG 2");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 14;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 3:
//Analog 3
displayLCDCenteredString(0, "ANALOG 3");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 15;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 4:
//Analog 4
displayLCDCenteredString(0, "ANALOG 4");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 16;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 5:
//Analog 5
displayLCDCenteredString(0, "ANALOG 5");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 17;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 6:
//Analog 6
displayLCDCenteredString(0, "ANALOG 6");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 18;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 7:
//Analog 7
displayLCDCenteredString(0, "ANALOG 7");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 19;
releaseCPU();
StartTask(display);
}
else
menuTracker();
break;
case 8:
//Analog 8
displayLCDCenteredString(0, "ANALOG 8");
displayLCDCenteredString(1, "< Enter >");
waitForPress();
if(nLCDButtons == centerButton)
{
waitForRelease();
portChoice = 20;
releaseCPU();
StartTask(display);
}
if(nLCDButtons == leftButton)
{
waitForRelease();
count--;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count = 0;
}
break;
}
}
}
//----------------------------------------------------------------
//Clear Ports-----------------------------------------------------
void clearPorts()
{
hogCPU();
SensorType[in1] = sensorNone;
SensorType[in2] = sensorNone;
SensorType[in3] = sensorNone;
SensorType[in4] = sensorNone;
SensorType[in5] = sensorNone;
SensorType[in6] = sensorNone;
SensorType[in7] = sensorNone;
SensorType[in8] = sensorNone;
SensorType[dgtl1] = sensorNone;
SensorType[dgtl2] = sensorNone;
SensorType[dgtl3] = sensorNone;
SensorType[dgtl4] = sensorNone;
SensorType[dgtl5] = sensorNone;
SensorType[dgtl6] = sensorNone;
SensorType[dgtl7] = sensorNone;
SensorType[dgtl8] = sensorNone;
SensorType[dgtl9] = sensorNone;
SensorType[dgtl10] = sensorNone;
SensorType[dgtl11] = sensorNone;
SensorType[dgtl12] = sensorNone;
releaseCPU();
}
//----------------------------------------------------------------
//Wait for Press--------------------------------------------------
void waitForPress()
{
while(nLCDButtons == 0);
{
wait1Msec(5);
}
}
//----------------------------------------------------------------
//Wait for Release------------------------------------------------
void waitForRelease()
{
while(nLCDButtons != 0);
{
wait1Msec(5);
}
}
//----------------------------------------------------------------
//Function to increment and decriment count - small savings on space
void menuTracker()
{
if(nLCDButtons == leftButton)
{
waitForRelease();
count--;
}
else if(nLCDButtons == rightButton)
{
waitForRelease();
count++;
}
}
//----------------------------------------------------------------
//Display---------------------------------------------------------
task display()
{
StopTask(analogPortChooser);
StopTask(digitalPortChooser);
hogCPU();
switch(portChoice){
case 1:
//Digital Port 1
digitalSensorSetup(dgtl1);
break;
case 2:
//Digital Port 2
digitalSensorSetup(dgtl2);
break;
case 3:
//Digital Port 3
digitalSensorSetup(dgtl3);
break;
case 4:
//Digital Port 4
digitalSensorSetup(dgtl4);
break;
case 5:
//Digital Port 5
digitalSensorSetup(dgtl5);
break;
case 6:
//Digital Port 6
digitalSensorSetup(dgtl6);
break;
case 7:
//Digital Port 7
digitalSensorSetup(dgtl7);
break;
case 8:
//Digital Port 8
digitalSensorSetup(dgtl8);
break;
case 9:
//Digital Port 9
digitalSensorSetup(dgtl9);
break;
case 10:
//Digital Port 10
digitalSensorSetup(dgtl10);
break;
case 11:
//Digital Port 11
digitalSensorSetup(dgtl11);
break;
case 12:
//Digital Port 12
digitalSensorSetup(dgtl12);
break;
case 13:
//Analog Port 1
analogSensorSetup(in1);
break;
case 14:
//Analog Port 2
analogSensorSetup(in2);
break;
case 15:
//Analog Port 3
analogSensorSetup(in3);
break;
case 16:
//Analog Port 4
analogSensorSetup(in4);
break;
case 17:
//Analog Port 5
analogSensorSetup(in5);
break;
case 18:
//Analog Port 6
analogSensorSetup(in6);
break;
case 19:
//Analog Port 7
analogSensorSetup(in7);
break;
case 20:
//Analog Port 8
analogSensorSetup(in8);
break;
}
releaseCPU();
StartTask(sensorChooser);
}
//----------------------------------------------------------------
//Creates the analog sensor to be displayed
void analogSensorSetup(tSensors sensorPort)
{
switch(sensorChoice){
case 3:
SensorType[sensorPort] = sensorLineFollower; //Change to sensorReflection
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Light:");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 4);
wait1Msec(100);
}
break;
case 4:
SensorType[sensorPort] = sensorLineFollower;
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Line Tracker:");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 4);
wait1Msec(100);
}
break;
case 5:
SensorType[sensorPort] = sensorPotentiometer;
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Potentiometer:");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 4);
wait1Msec(100);
}
break;
case 6:
SensorType[sensorPort] = sensorAccelerometer;
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Accelerometer:");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 4);
wait1Msec(100);
}
break;
}
}
//----------------------------------------------------------------
//Creates the digital sensor to be displayed----------------------
void digitalSensorSetup(tSensors sensorPort)
{
switch(sensorChoice){
case 0:
SensorType[sensorPort] = sensorTouch;
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Touch:");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 1);
wait1Msec(100);
}
break;
case 1:
SensorType[sensorPort] = sensorQuadEncoder;
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Quad Encoder:");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 6);
wait1Msec(100);
}
break;
case 2:
SensorType[sensorPort] = sensorSONAR_cm;
clearLCDLine(0);
displayLCDPos(0,0);
displayNextLCDString("Ultrasonic (cm):");
while(nLCDButtons != 2)
{
clearLCDLine(1);
displayLCDPos(1,0);
displayNextLCDNumber(SensorValue[sensorPort], 4);
wait1Msec(100);
}
break;
}
}
//----------------------------------------------------------------
Dancing VEX robot: Bear Bot [Team 4542]
Thanks to magiccode from the forums for posting this!
Overview
“Our robotics team made a semi-humanoid dancing VEX robot with a holonomic drive in place of legs. It has full range of motion in both arms and two planes of motion in its head. It can bend at the waist, and strafe or turn in any direction.
It dances. plays the piano and beats little kids up. He is an all around entertainer. We only had about a day to program him, so bear with us… pun intended”
Video
Description
There were 3 “cool” things that were done with ROBOTC:
- The robot mimics the movements of a human arm which is holding the VexNet joystick or Vex accelerometer. This will not work in all directions if the joytick is being used because the joystick lacks a z-axis, but it will work in all directions if the accelerometer is being used.
- There were too many motors to be controlled by one cortex, so we linked two together by running a male to male pwm wire from the digital output port of one to the digital input port of the other.
- Programming was made easier by writing a function called moveServo(). The function would accept 3 parameters: the servo to move, the position to which it should move, and the amount of time it should take (does not take into account changes in battery power)
Code:
moveServo(tMotor servoName, int posToMove, int timeToTake);




