Page 2 of 2

Re: NXC and Braking

Posted: 19 Feb 2012, 19:56
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

Re: NXC and Braking

Posted: 19 Feb 2012, 20:45
by lamikam
Thanks for the input!

Re: NXC and Braking

Posted: 19 Feb 2012, 21:04
by h-g-t
John, that looks pretty much what I was after but does it work with th standard firmware?

Re: NXC and Braking

Posted: 19 Feb 2012, 22:35
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.)

Re: NXC and Braking

Posted: 20 Feb 2012, 03:51
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

Re: NXC and Braking

Posted: 20 Feb 2012, 13:44
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?

Re: NXC and Braking

Posted: 20 Feb 2012, 18:11
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