Home > Ideas, Mindstorms > NXT Automated Gearbox

NXT Automated Gearbox

February 25th, 2012 Leave a comment Go to comments

NXT Automated Gearbox

My first MOC using the LEGO Mindstorms NXT. Features a simple 4-speed gearbox that shifts automatically based on changes in the speed of the motor driving the gearbox.  

Datasheet:

Completion date: 25/02/2012
Power: electric (NXT brick)
Language: ROBOTC
Bricks: 1
Motors: 3 x NXT motor
Sensors: none

Having recently finally bought the LEGO Mindstorms NXT set, I have been looking for an opportunity to practice some programming. The default NXT-G software was a nightmare to me, so I was hesitating between leJOS and ROBOTC, finally settling for the latter.

The gearbox uses my 4-speed compact gearbox design, together with two motors that change speeds and a third motor that drives the gearbox and whose speed is being monitored by the program.

The program, which can be found complete below, consists of three task: one main and two subtasks included in it. One subtask monitors the use of the NXT brick’s buttons (because the program replaces their default functionality), while the other monitors the voltage of the brick’s battery (which can change quite a lot, depending on the load on the motors).  This is my first ROBOTC program, and I suppose I will be using these two subtasks on a regular basis.

The main tasks launches the two subtasks and controls all three motors. Once the propulsion motors is started, it monitors its speed counting the amount of degrees of rotation 10 times per second. Thus the basic speed unit is degrees/0.1 second, and depending on the condition of the batteries I’m using it’s usually around 70. There are two threshold speed values declared in the program, and if the motor exceeds one of them, the gearbox shifts up or down accordingly. Then it waits for a given interval to allow the motor to work a while with the changed gear ratio, and gets back to monitoring its speed.

The three values – lower and upper speed threshold value and the shifting interval –  are declared right at the beginning of the program for easy adjustment. I found it to be convenient and I will be probably doing so in my future programs as well.

The gearbox is synchronized and non-sequential, so it can be switched from any speed to any other speed. For simplicity, or more specifically to limit the number of possible combinations, I have designed the program to switch only between “adjacent” speeds – so it can swicth from 3rd to 4th or from 3rd to 2nd, but not from 3rd to 1st. There is one exception: shutting down. The program can be instructed to shut down by pressing the brick’s default exit button, and upon doing so it shifts the gearbox down to 1st. Obviously, to do so it needs to keep tracks of the currently selected gear – this information is stored in the variable gear, and updated when shifting down or up.

Programming the gearbox was an interesting experience and it taught me a lot about ROBOTC. I was surprised to found that rotating the “control” motors by a given degree proved less accurate than rotating them for a given time interval. By adjusting precisely their running time, I was able to achieve great accuracy needed to shift the transmission driving rings in the gearbox without pushing them too strongly into other parts of the gearbox.

Program:

// config start
int mspeed_high = 78;
int mspeed_low = 75;
int shift_time = 3000;
// config end

int gear = 1;

task Buttons()
{
  while(true)
  {
    nNxtButtonTask  = -2;
    nNxtExitClicks = 2;

    if(nNxtButtonPressed == 0)
    {
      eraseDisplay();
      motor[motorA] = 0;

            switch(gear)
            {
              case 1:
                break;

              case 2:
                motor[motorB] = -100;
                wait1Msec(120);
                motor[motorB] = 0;
                break;

              case 3:
                motor[motorC] = 100;
                wait1Msec(60);
                motor[motorC] = 0;
                wait1Msec(500);
                motor[motorB] = -100;
                wait1Msec(70);
                motor[motorB] = 0;
                break;

              case 4:
                motor[motorC] = -100;
                wait1Msec(60);
                motor[motorC] = 0;
                wait1Msec(500);
                motor[motorB] = -100;
                wait1Msec(70);
                motor[motorB] = 0;
                break;
            }

      StopAllTasks();
    }
  }
  return;
}

task BatLev()
{
  while(true)
  {
    nxtDisplayCenteredTextLine(0, "Brick up @ %3.1fV", nImmediateBatteryLevel / (float) 1000);
    wait1Msec(50);
  }
  return;
}

task main ()
{
  StartTask(Buttons);
  StartTask(BatLev);

  motor[motorA] = 0;
  bool mstatus = false;
  nMotorEncoder[motorB] = 0;
  nMotorEncoder[motorC] = 0;
  nxtDisplayCenteredTextLine(2, "MOTOR A OFF");
  nxtDisplayCenteredTextLine(4, "GEAR 1");
  wait1Msec(50);

  while(true)
  {
    if(nNxtButtonPressed == 3)
    {
      if (mstatus == true)
      {
        PlaySound(soundBlip);
        motor[motorA] = 0;
        mstatus = false;
        nxtDisplayCenteredTextLine(2, "MOTOR A OFF");
        nxtDisplayClearTextLine(5);
        nxtDisplayClearTextLine(7);
        wait1Msec(500);
      }
      else
      {
        motor[motorA] = 100;
        mstatus = true;
        nxtDisplayCenteredTextLine(2, "MOTOR A ON");
        wait1Msec(500);
        int mspeed = nMotorEncoder[motorA];

        while(nNxtButtonPressed != 3)
        {
          nMotorEncoder[motorA] = 0;
          wait1Msec(100);
          mspeed = nMotorEncoder[motorA];
          nxtDisplayCenteredTextLine(5, "Speed: %d", mspeed);

          if (mspeed > mspeed_high) // gearing up
          {
            nxtDisplayCenteredTextLine(7, "Gearing up...");
            switch(gear)
            {
              case 1:
                motor[motorB] = 100;
                wait1Msec(110);
                motor[motorB] = 0;
                gear = 2;
                nxtDisplayCenteredTextLine(4, "GEAR 2");
                wait1Msec(shift_time);
                break;

              case 2:
                gear = 3;
                motor[motorB] = -100;
                wait1Msec(60);
                motor[motorB] = 0;
                wait1Msec(300);
                motor[motorC] = -100;
                wait1Msec(60);
                motor[motorC] = 0;
                nxtDisplayCenteredTextLine(4, "GEAR 3");
                wait1Msec(shift_time);
                break;

              case 3:
                motor[motorC] = 100;
                wait1Msec(110);
                motor[motorC] = 0;
                gear = 4;
                nxtDisplayCenteredTextLine(4, "GEAR 4");
                wait1Msec(shift_time);
                break;

              case 4:
                nxtDisplayClearTextLine(7);
                break;
            }
          }
          else if (mspeed < mspeed_low) // gearing down
          {
            nxtDisplayCenteredTextLine(7, "Gearing down...");
            switch(gear)
            {
              case 4:
                motor[motorC] = -100;
                wait1Msec(110);
                motor[motorC] = 0;
                gear = 3;
                nxtDisplayCenteredTextLine(4, "GEAR 3");
                wait1Msec(shift_time);
                break;

              case 3:
                motor[motorC] = 100;
                wait1Msec(60);
                motor[motorC] = 0;
                wait1Msec(300);
                motor[motorB] = 100;
                wait1Msec(60);
                motor[motorB] = 0;
                gear = 2;
                nxtDisplayCenteredTextLine(4, "GEAR 2");
                wait1Msec(shift_time);
                break;

              case 2:
                motor[motorB] = -100;
                wait1Msec(110);
                motor[motorB] = 0;
                gear = 1;
                nxtDisplayCenteredTextLine(4, "GEAR 1");
                wait1Msec(shift_time);
                break;

              case 1:
                nxtDisplayClearTextLine(7);
                break;
            }
          }
        }
      }
    }
  }
}
									

Photos:

1.jpg dsc08349.jpg dsc08353.jpg dsc08358.jpg

Video:

YouTube Preview Image
Categories: Ideas, Mindstorms Tags: ,
  1. Sariel
    March 23rd, 2012 at 17:14 | #1

    @maxitechnic
    Well, then you have to copy the code I posted and paste it into new ROBOTC files.

  2. maxitechnic
    March 23rd, 2012 at 16:36 | #2

    thank you for your reply
    I know vaguely RobotC.@Sariel

  3. Sariel
    March 22nd, 2012 at 20:49 | #3

    @maxitechnic
    Do you know anything about ROBOTC?

  4. maxitechnic
    March 22nd, 2012 at 19:20 | #4

    hello
    I would like to save this program in my nxt brick, but I do not know how to proceed. Can you help me

  5. Eamon
    March 13th, 2012 at 02:55 | #5

    you should incorporate this into a smaller veicle. maybe one day we’ll have LEGO drag racers that do a 4 meter instead of a quarter-mile

  6. March 3rd, 2012 at 12:40 | #6

    Pretty cool, I just got the Energy Meter. It can measure Amps which can loosely translate to torque. I when I get back home I would like to replicate this with two servos for shifting and a PF motor for driving. Hopefully the energy meter will sense the load based on the amps being drawn and adjust similar to yours, but with out the voltage variable.

  7. Sariel
    February 26th, 2012 at 22:35 | #7

    @Ed
    There is a link right up there in the second paragraph of the text.

  8. Ed
    February 26th, 2012 at 21:50 | #8

    sorry for the stupid question, but in the video description it says that there are instructions. I can’t find them. Help?

  9. Luke321
    February 26th, 2012 at 18:32 | #9

    @Luke321
    just one more thing, you could use the absolute Encoder Values of the Nxt motors, combined with your own motor controller to achieve the best precision.
    And yes, RobotC’s build in Motor Control is not the best, especially when you have high loads…

  10. Luke321
    February 26th, 2012 at 18:23 | #10

    i want to do something similar when i’m done with my current project using this modification of your gearbox design
    http://www.youtube.com/watch?v=Vc3_rxaF36c
    with one motor you could change all 4 gears and use the other 2 for driving…
    and nice to see that someone else uses RobotC!

  11. Sariel
    February 25th, 2012 at 21:11 | #11

    @Spiller
    Thank you for this insight. I’m basing my usage of the ROBOTC mainly on my experience with PHP, so using multiple tasks is something new to me.

  12. February 25th, 2012 at 18:29 | #12

    I realize this was just for practice but it would have been more useful if you used a gearbox design which only required one motor to change gears. A sequential with at least 3 gears would be interesting.

    A few notes about the programming:
    For the configuration variables use const so that they cannot be changed later in the program.
    Secondly, avoid using multiple tasks when you can as it is far more error prone than people realize. Consider what would happen if tells it to shut down while it is shifting gear. The two tasks will both try to control the motors so they will end up interfering with each other and the results will be (in practice) unpredictable.
    If you want to work with robotics regularly I strongly recommend you to read a bit about concurrent programming so you know the pitfalls and how to prevent them as you often can’t avoid having multiple tasks completely.
    I’m mentioning this in particular as people new to Mindstorms tend to add a task for every problem they need to solve without knowing the issues this can cause.

  1. No trackbacks yet.