NXC and Braking

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: NXC and Braking

Post by afanofosc »

Here's a link to my post with the ramping example.

https://sourceforge.net/apps/phpbb/mind ... ing#p11771

Included inline for easy access:

Code: Select all


void RotateMotorRURD(byte output, char pwr, long angle)
{
  long l1, l2, l3;
  char power = sign(angle);
  angle = abs(angle);
  power *= pwr;

  l1 = angle*0.20;
  l2 = angle*0.60;
  l3 = angle-(l1+l2);
  // we want to rotate a total of <angle> degrees
  // we'll rampup from 0 power to specified power through 20% of the angle
  // then run at the specified power for 70% of the angle
  SetOutput(output, OutputModeField, OUT_MODE_MOTORON|OUT_MODE_BRAKE,
                    TachoLimitField, l1,
                    PowerField, power,
                    RegModeField, OUT_REGMODE_IDLE,
                    RunStateField, OUT_RUNSTATE_RAMPUP,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  // monitor runstate
  while(MotorRunState(output) <> OUT_RUNSTATE_IDLE)
    Yield();
  // as soon as it goes idle put the motor into the running state
  SetOutput(output, OutputModeField, OUT_MODE_MOTORON|OUT_MODE_BRAKE,
                    TachoLimitField, l2,
                    PowerField, power,
                    RegModeField, OUT_REGMODE_IDLE,
                    RunStateField, OUT_RUNSTATE_RUNNING,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  // monitor runstate
  while(MotorRunState(output) <> OUT_RUNSTATE_IDLE)
    Yield();
  // as soon as the runstate goes idle we rampdown to zero power
  SetOutput(output, OutputModeField, OUT_MODE_MOTORON|OUT_MODE_BRAKE,
                    TachoLimitField, l3,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_IDLE,
                    RunStateField, OUT_RUNSTATE_RAMPDOWN,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  // monitor runstate
  while(MotorRunState(output) <> OUT_RUNSTATE_IDLE)
    Yield();
  SetOutput(output, OutputModeField, OUT_MODE_MOTORON+OUT_MODE_BRAKE+OUT_MODE_REGULATED,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_RUNNING,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  long rc = MotorRotationCount(output);
  long oldrc = rc+1;
  while (oldrc <> rc) {
    oldrc = rc;
    Wait(10);
    rc = MotorRotationCount(output);
  }
  SetOutput(output, OutputModeField, OUT_MODE_COAST+OUT_MODE_REGULATED,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_IDLE,
                    UpdateFlagsField, UF_UPDATE_MODE);
  Yield(); // give firmware a chance to process this request to update motor state
}

bool bDone = false;

task MoveMotors()
{
  RotateMotorRURD(OUT_A, 75, 720);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, -720);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, 720);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, -720);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, 720);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, -720);
  Wait(500);
  bDone = true;
}

task MonitorMotors()
{
  while (!bDone) {
    ClearScreen();
    NumOut(0, LCD_LINE1, MotorRunState(OUT_A));
    NumOut(0, LCD_LINE2, MotorMode(OUT_A));
    NumOut(0, LCD_LINE3, MotorRotationCount(OUT_A));
    Wait(1);
  }
}

task main()
{
  Precedes(MoveMotors, MonitorMotors);
  ResetRotationCount(OUT_A);
  Wait(5);
}
So in the above code I brake after ramping down but then, as written, it idles the motors which would release the brake. You can modify this as needed for your purposes. However, if your robotic arm requires power levels > 75 then ramping up and down may not work at all for you. Or maybe you could ramp down to something > 0 from a high power level across a much shorter percentage of the total tachometer rotation and then apply the brake.

Or you could stick with your approach of Coast/Off/Brake which you mention above.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
lamikam
Posts: 7
Joined: 18 Feb 2012, 19:58

Re: NXC and Braking

Post by lamikam »

Thanks for the input!
h-g-t
Posts: 552
Joined: 07 Jan 2011, 08:59
Location: Albania

Re: NXC and Braking

Post by h-g-t »

John, that looks pretty much what I was after but does it work with th standard firmware?
A sophistical rhetorician, inebriated with the exuberance of his own verbosity, and gifted with an egotistical imagination that can at all times command an interminable and inconsistent series of arguments to malign an opponent and to glorify himself.
mcsummation
Posts: 220
Joined: 23 Jan 2012, 17:07
Location: Round Rock, TX

Re: NXC and Braking

Post by mcsummation »

John, I'm running your code to turn a turntable that has a 7:1 reduction from the motor. I run it for 360 degrees of the turntable (7*360 = 2520 degrees on the motor) and monitor the count on the LCD. I run it forward, then backwards with a 1000 ms wait between the 2 directions. It never returns to 0. Should it? (Forward gives 2525 and back, without reset, gives -8, +/- 2.) I get even larger errors using RotateMotor.

(I'm working on the Odin robot http://www.philohome.com/odin/odin.htm, programmed in NXC instead of NXT-G, and I have been getting small errors in the distances and angles that it runs. I was hoping that your routine might help with the errors. I have not added your code to the robot's code, just making sure I understand your code first.)
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: NXC and Braking

Post by afanofosc »

The code I posted above is standard firmware compatible. The enhanced firmware will use native opcodes for sign and abs but the compiler generates code that produces the equivalent results with the standard firmware.

It looks like giving the motor more time to settle (if oscillation is okay) when the brake is applied can help with how close the motor actually adheres to the specified tachometer target.

Code: Select all

void RotateMotorRURD(byte output, char pwr, long angle, bool UseSpeedControl = true)
{
  long l1, l2, l3;
  char power = sign(angle);
  angle = abs(angle);
  power *= pwr;

  if (angle > 720)
    l1 = angle*0.10;
  else
    l1 = angle*0.20;
  l3 = l1;
  l2 = angle-(l1+l3);
  byte om = OUT_MODE_MOTORON|OUT_MODE_BRAKE;
  byte rm = OUT_REGMODE_IDLE;
  if (UseSpeedControl) {
    om += OUT_MODE_REGULATED;
    rm = OUT_REGMODE_SPEED;
  }
  // we want to rotate a total of <angle> degrees
  // we'll rampup from 0 power to specified power through 20% of the angle
  // then run at the specified power for 60% of the angle
  SetOutput(output, OutputModeField, om,
                    TachoLimitField, l1,
                    PowerField, power,
                    RegModeField, rm,
                    RunStateField, OUT_RUNSTATE_RAMPUP,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  // monitor runstate
  while(MotorRunState(output) <> OUT_RUNSTATE_IDLE)
    Yield();
  // as soon as it goes idle put the motor into the running state
  SetOutput(output, OutputModeField, om,
                    TachoLimitField, l2,
                    PowerField, power,
                    RegModeField, rm,
                    RunStateField, OUT_RUNSTATE_RUNNING,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  // monitor runstate
  while(MotorRunState(output) <> OUT_RUNSTATE_IDLE)
    Yield();
  // as soon as the runstate goes idle we rampdown to zero power
  SetOutput(output, OutputModeField, om,
                    TachoLimitField, l3,
                    PowerField, 0,
                    RegModeField, rm,
                    RunStateField, OUT_RUNSTATE_RAMPDOWN,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  // monitor runstate
  while(MotorRunState(output) <> OUT_RUNSTATE_IDLE)
    Yield();
  // now apply powered braking for a little while
  SetOutput(output, OutputModeField, OUT_MODE_MOTORON+OUT_MODE_BRAKE+OUT_MODE_REGULATED,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_RUNNING,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  long rc = MotorRotationCount(output);
  long oldrc = rc+1;
  while (oldrc <> rc) {
    oldrc = rc;
    Wait(100);  // adjust this wait time to see what impact it has on accuracy.
    rc = MotorRotationCount(output);
  }
  // finally, go idle
  SetOutput(output, OutputModeField, OUT_MODE_COAST+OUT_MODE_REGULATED,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_IDLE,
                    UpdateFlagsField, UF_UPDATE_MODE);
  Yield(); // give firmware a chance to process this request to update motor state
}

bool bDone = false;

task MoveMotors()
{
  RotateMotorRURD(OUT_A, 75, 2520);
  Wait(2000);
  RotateMotorRURD(OUT_A, 75, -2520);
  Wait(2000);
/*
  RotateMotorRURD(OUT_A, 75, 720, false);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, -720, false);
  Wait(500);
*/
  RotateMotorRURD(OUT_A, 75, 2520);
  Wait(2000);
  RotateMotorRURD(OUT_A, 75, -2520);
  Wait(2000);
/*
  RotateMotorRURD(OUT_A, 75, 720, false);
  Wait(500);
  RotateMotorRURD(OUT_A, 75, -720, false);
  Wait(500);
*/
  bDone = true;
}

task MonitorMotors()
{
  while (!bDone) {
    ClearScreen();
    NumOut(0, LCD_LINE1, MotorRunState(OUT_A));
    NumOut(0, LCD_LINE2, MotorMode(OUT_A));
    NumOut(0, LCD_LINE3, MotorRotationCount(OUT_A));
    NumOut(0, LCD_LINE4, MotorTachoCount(OUT_A));
    NumOut(0, LCD_LINE5, MotorBlockTachoCount(OUT_A));
    Wait(10);
  }
}

task main()
{
  Precedes(MoveMotors, MonitorMotors);
  ResetRotationCount(OUT_A);
  Wait(5);
}
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
mcsummation
Posts: 220
Joined: 23 Jan 2012, 17:07
Location: Round Rock, TX

Re: NXC and Braking

Post by mcsummation »

The time at idle (at the end) doesn't seem to matter. My motor always overshoots by an average of 4 degrees. So, I adjusted the rampdown angle by -4. So, the rampdown SetOutput is

Code: Select all

  SetOutput(output, OutputModeField, om,
                    TachoLimitField, l3-4,
                    PowerField, 0,
                    RegModeField, rm,
                    RunStateField, OUT_RUNSTATE_RAMPDOWN,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
I wanted braking at the end for one motor, coasting for another, so I changed the function statement and changed the final SetOutput to:

Code: Select all

void RotateMotorRURD(byte output, char pwr, long angle, bool UseSpeedControl = true, bool BrakeAtEnd = true)
...
  // finally, go idle
  om = OUT_MODE_REGULATED;
  if (BrakeAtEnd)
    om |= OUT_MODE_BRAKE;
  else
    om |= OUT_MODE_COAST;
  SetOutput(output, OutputModeField, om,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_IDLE,
                    UpdateFlagsField, UF_UPDATE_MODE);
  Yield(); // give firmware a chance to process this request to update motor state
Does that look right?
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: NXC and Braking

Post by afanofosc »

That does not look right. Can you tell in my routine where I apply the brake? It is right after the comment that says "now apply powered braking for a little while.

The "little while" in my original code was as short as 10 ms. Changing that to be no shorter than 100ms made a huge difference the accuracy of where the motor finally completely stopped. The LCD output would consistently read 0 or 1 or 2520 or 2519 when I changed the wait inside the while loop after applying the brakes. You should see the same results I was seeing if you try it with a motor attached to A and run the code as I posted it. Then compare it to my original code. I think this is because the motor control routine does not execute every millisecond or even every 10 ms so if you don't give it long enough to actually adjust the motor position then it will stay in the wrong place.

If you want to optionally idle the motors at the end of the routine based on a boolean value passed in then just wrap the last little bit of code in an if statement.

Code: Select all

  if (!BrakeAtEnd)
  {
  // finally, go idle
  SetOutput(output, OutputModeField, OUT_MODE_COAST+OUT_MODE_REGULATED,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_IDLE,
                    UpdateFlagsField, UF_UPDATE_MODE);
  Yield(); // give firmware a chance to process this request to update motor state
  }
If you want this BrakeAtEnd parameter to stop the powered braking also then wrap that bit in an if statement as well.

Code: Select all

  if (BrakeAtEnd)
  {
  // now apply powered braking for a little while
  SetOutput(output, OutputModeField, OUT_MODE_MOTORON+OUT_MODE_BRAKE+OUT_MODE_REGULATED,
                    TachoLimitField, 0,
                    PowerField, 0,
                    RegModeField, OUT_REGMODE_SPEED,
                    RunStateField, OUT_RUNSTATE_RUNNING,
                    UpdateFlagsField, UF_UPDATE_MODE|UF_UPDATE_SPEED|UF_UPDATE_TACHO_LIMIT);
  Yield(); // give firmware a chance to process this request to update motor state
  long rc = MotorRotationCount(output);
  long oldrc = rc+1;
  while (oldrc <> rc) {
    oldrc = rc;
    Wait(100);  // adjust this wait time to see what impact it has on accuracy.
    rc = MotorRotationCount(output);
  }
  }
With these two sections of code controlled by the parameter you pass in then if you pass in false it will let the motor coast to a stop once it reaches the rampdown tachometer limit but if you pass in true it will apply the brake and leave it on when the routine is over. You can remove the brake via Coast(port) in a separate call if you like.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
Post Reply

Who is online

Users browsing this forum: No registered users and 30 guests