Another ramp question

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
Post Reply
mcsummation
Posts: 220
Joined: 23 Jan 2012, 17:07
Location: Round Rock, TX

Another ramp question

Post by mcsummation »

I want to ramp up a motor, then leave it running. I took the code you had provided earlier for the ramp up/down to a tacho limit and lifted just the ramp up part.

Code: Select all

// Ramp up for a motor.

void RampUp(byte output,
            char pwr,
            long angle,            // Number of degrees to do the rampup.
            bool UseSpeedControl = true)
{
    char power = sign(angle);
    angle = abs(angle);
    power *= pwr;

    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
    SetOutput(output, OutputModeField, om,
                    TachoLimitField, angle,
                    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
    OnFwd(output, power);
}

task RampB()
{
    RampUp(OUT_B, 50,1800);
}

task main()
{
    SetSensorTouch(S4);
    StartTask(RampB);
    until(SensorBoolean(S4)) Yield();
    StopTask(RampB);
    Off(OUT_B);
}
When I bump the touch sensor that is plugged into port 4, the motor is stopped, but the program continues to run. It appears that the RampB task is not killed.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Another ramp question

Post by afanofosc »

This does not sound like a motor control questions related to ramping up but, rather, a question about task starting/stopping.

I have frequently tried to persuade people not to use the NQC keywords and functions that I have implemented in NXC and the enhanced firmware for manually starting and stopping tasks. There might be a firmware problem related to manually stopping a task using StopTask. I don't know for certain one way or another. You are way better off to use Precedes and Follows since those techniques are part of the original design of the firmware VM.

Also, in your case, task B stops on its own as soon as it completes the one thing you asked it to do. Are you pressing the touch sensor before it has completed the 1800 degree ramp up? If a task doesn't run forever it is best to let it end on its own.

If you want motor B to stop turning when it causes a touch sensor to be pressed then put that logic into the same task that starts it moving.

If you want to end your program when the motor stops turning due to the touch sensor having been pressed then you can set a global "taskBDone" boolean variable or something like that and wait in a loop until it is true.

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: Another ramp question

Post by mcsummation »

Well, it was a motor control question because I wanted to use the "ramp up" and be able to stop the motor control during the ramp up and not go on to the "continue run" phase.

This program is just a "demonstration" of the problem, using the simplest sensor and control that I have available on the robot. The actual trigger comes in from the color sensor detecting a black line. The rotation sensor is captured, the motor is stopped, and then the program goes on with following things. This video shows what the original builder's robot does - http://www.philohome.com/odin/raz.avi. I'm wanting to use the ramp up/down to make the motions smoother as it starts and stops on the paper. However, it's possible that the black line is detected during the ramp up and I need to stop the motor (actually I'm going to do a quick ramp down - probably only a 10-20 degree rotation). The 1800 degree ramp up was just so I can tell what's actually happening during the "kill ramp up".

I can't get my head wrapped around starting the "ramp up" task using only Precedes/Follows. Can you give some pseudo-code that does it? Or, does the StartTask work properly? It seems to.

Here's the version of the program using a flag instead of StopTask.

Code: Select all

// Ramp up for a motor.

bool bStopRampB;

void RampUp(byte output,
            char pwr,
            long angle,            // Number of degrees to do the rampup.
            bool UseSpeedControl = true)
{
    char power = sign(angle);
    angle = abs(angle);
    power *= pwr;

    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
    SetOutput(output, OutputModeField, om,
                    TachoLimitField, angle,
                    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)
    {
        if (bStopRampB)
        {
            Off(OUT_B);
            return;
        }
        Yield();
    }

    // as soon as it goes idle put the motor into the running state
    OnFwd(output, power);
}

task RampB()
{
    RampUp(OUT_B, 50, 1800);
}

task main()
{
    SetSensorTouch(S4);
    bStopRampB = false;
    StartTask(RampB);
    until(SensorBoolean(S4)) Yield();
    bStopRampB = true;
//    StopTask(RampB);
    Off(OUT_B);
    Wait(1000);            // This is just so I can see the motor stop before the program exits.
}
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Another ramp question

Post by afanofosc »

Sorry I didn't understand your question.

Instead of writing a function that does 3 separate things - one of which takes a long time, depending on the angle specified - I would generally recommend that you code this as three distinct operations. Have the loop check for all conditions that should make your code behave differently. Only call the third part if the conditions indicate it should be called.

If you have code like this:

Code: Select all

task play() {
  while (true) {
    PlayTone(TONE_A4, MS_500);
    Wait(SEC_1);
  }
}

task main()
{
  start play; // StartTask(play);
  while (true) {
    OnFwd(OUT_A, 50);
    Yield();
  }
}
You can convert it to use Precedes like this:

Code: Select all

task play() {
  while (true) {
    PlayTone(TONE_A4, MS_500);
    Wait(SEC_1);
  }
}

task drive()
{
  while (true) {
    OnFwd(OUT_A, 50);
    Yield();
  }
}

task main()
{
  Precedes(play, drive);
}
In other words, take everything in the current task after the call to StartTask(task1) and put it in another task (task2) and replace StartTask(task1) with Precedes(task1, task2). Everything before StartTask can stay where it was at. The call to StopTask(x) can be replaced by setting a global variable associated with that task to TRUE and then check that variable frequently in the associated task and exit the task if it ever becomes TRUE. If you want some bit of code to execute as soon as two other tasks have both completed all their work then put your code in a task and at the start of the task indicate that it Follows(task1, task2, etc...) listing all the task names that should finish running before the current task starts running. If you need to start your program over again then have your last task ExitTo(firstTask) where firstTask is the name of the task that gets everything started up correctly. I posted a sample program that demonstrates this a few days ago somewhere in this forum.

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: Another ramp question

Post by mcsummation »

OK, I've made changes like what you've said to avoid the use of StopTask.

However, this statement
afanofosc wrote:The call to StopTask(x) can be replaced by setting a global variable associated with that task to TRUE and then check that variable frequently in the associated task and exit the task if it ever becomes TRUE.
bothers me because I would like the RampUp routine to be a little more general. Can I pass the "name" of the bool into the parameter list so it can be set from outside the routine and used inside? I haven't found a way to do it, but there are so many differences between NXC and the "C" variants I've used in the past that I probably just missed it. I thought "pass by reference" would do it but my tests indicate otherwise.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: Another ramp question

Post by afanofosc »

I'm inclined to think there is no point at all to a function that somehow needs to be interruptible. That's just not something you see except where something global like an event object is used in a while loop. Instead, I would recommend that you have a function that starts the rampup and exits. A custom loop (not a standard function) that waits for the rampup to finish and also checks your interrupt conditions. Then a standard function that does the third part (OnFwd).

Or something like this:

Code: Select all


bool __RampInterrupt = false;

// Ramp up for a motor.

inline void InterruptRampUp() { __RampInterrupt = true; }

void RampUp(byte output,
            char pwr,
            long angle,            // Number of degrees to do the rampup.
            bool UseSpeedControl = true)
{
    __RampInterrupt = false;
    char power = sign(angle);
    angle = abs(angle);
    power *= pwr;

    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
    SetOutput(output, OutputModeField, om,
                    TachoLimitField, angle,
                    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();
        if (__RampInterrupt) return;
    }

    // as soon as it goes idle put the motor into the running state
    OnFwd(output, power);
}

task RampB()
{
    RampUp(OUT_B, 50,1800);
}

task main()
{
    SetSensorTouch(S4);
    StartTask(RampB);
    until(SensorBoolean(S4)) Yield();
    InterruptRampup();
//    StopTask(RampB);
    Off(OUT_B);
}
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: Another ramp question

Post by mcsummation »

After sleeping on your last couple of responses, I realized that I had complicated the problem. I now have 2 routines, one that starts/does the ramp up and one to do a "continue" when the ramp up has completed. I did the little "continue" routine simply to make the user code cleaner.

Code: Select all

void RampUp(byte output,
            char pwr,
            long angle,             // Number of degrees to do the rampup.
            bool WaitForCompletion = false,
            bool UseSpeedControl = true)
{
    // Ramp up for a motor.
    char power = sign(angle);
    angle = abs(angle);
    power *= pwr;

    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
    SetOutput(output, OutputModeField, om,
                    TachoLimitField, angle,
                    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

    if (WaitForCompletion)
    {
        // monitor runstate
        while(MotorRunState(output) != OUT_RUNSTATE_IDLE) Yield();
    }
}

void ContinueRun(byte output, char pwr)
{
    // If RampUp has completed, turn motor on.
    if (MotorRunState(output) == OUT_RUNSTATE_IDLE)
        OnFwd(output, pwr);

}
The user code now looks like this:

Code: Select all

#include "RampUpRampDown.nxc"

task main()
{
    SetSensorTouch(S4);
    RampUp(OUT_B, 50, 1800);
    until(SensorBoolean(S4))
    {
        ContinueRun(OUT_B, 50);
        Yield();
    }
    Off(OUT_B);
    Wait(1000);
}
mcsummation
Posts: 220
Joined: 23 Jan 2012, 17:07
Location: Round Rock, TX

Re: Another ramp question

Post by mcsummation »

I was doing some experiments with the ramp down piece of code (just that one piece). I had the motor running at pwr=30 and then invoked "ramp down to 0 over 2000 degrees". It appeared to speed up to about pwr=60 (or some such speed), then start the ramp down. Does that make sense?

Don't worry, I decided that Coast better suits my needs than a RampDown subroutine.
Post Reply

Who is online

Users browsing this forum: Semrush [Bot] and 0 guests