View unanswered posts | View active topics It is currently Thu Jul 31, 2014 3:40 pm






Reply to topic  [ 14 posts ] 
one button controlling help 
Author Message
Rookie

Joined: Sat Nov 17, 2012 10:27 pm
Posts: 5
Post one button controlling help
Hi! This year is my first year Owasso, OK has ever had a robotics team. I happen to be the one and only programmer. Unfortunately, no one has past experience with robotc. Fortunately, I've learned some of it.

But back to the problem; I'm trying to control a tetrix servo with one button, where if you press it once, it moves to a certain position. And if you press it another time, it moves back to the original position. This is what I currently have:

Code:
      if(joy1Btn(3))
      {
         if(servo[servo4] > 60)
         {
            servo[servo4]=20;
         }
         else
            servo[servo4]=100;


This works, just not perfectly. When I press the button, it hesitates, and sometimes doesn't even go. So usually, I press it once quickly, and either it shakes and moves to the position, or it just shakes and stays there. Can anyone help me make a better way to program this? Any contribution will be greatly appreciated.

Thank you. :bigthumb:


Thu Dec 13, 2012 10:29 pm
Profile
Moderator
Moderator
User avatar

Joined: Tue Sep 14, 2010 9:19 pm
Posts: 496
Post Re: one button controlling help
The "problem" here is that the processor is runs through your code extremely quickly. Even when you press the button really fast, the loop executes many times during that press. This means that the value is continuously switched between 20 and 100. Let me know if you want a more in depth explanation of this.

Here is some pseudocode showing how to accomplish what you want to do:

Code:
bool buttonAlreadyPressed = false; // boolean variable to hold the previous state of the button
while(true){
  if(button is pressed){
    if(!buttonAlreadyPressed){ //Only execute if the button is being pressed for the first time since being released
      buttonAlreadyPressed = true; // now it's been pressed, so don't execute this next time, until you release and press it again
      if(ServoValue[servo4] > 60){
        servo[servo4] = 20;
      }
      else{
        servo[servo] = 100;
      }
    }
  }
  else{  //button isn't pressed
    buttonAlreadyPressed = false; // now the button has been released
  }
}
       

_________________
sudo rm -rf /


Thu Dec 13, 2012 11:31 pm
Profile
Guru
User avatar

Joined: Sun Nov 15, 2009 5:46 am
Posts: 1347
Post Re: one button controlling help
Another way to look at it is to break the problem down to the following sub problems:
1. Declare a variable that remembers the current position of the motor and program the motor to the current position specified by this variable.
2. Program a button to toggle the variable to the two possible positions when it is pressed.

So let's say you have an arm controlled by a servo motor and you have two possible positions: arm up (0) and arm down (120).

To solve problem 1, the code looks something like this:
Code:
int armPos = 0;

while (true)
{
    servo[armServo] = armPos;
    wait1Msec(100);
}

To solve problem 2 is a little trickier. Your code must detect a button transition event instead of monitoring if the button is pressed. Like Magicode said, the processor runs really fast. If you toggle the position on each robot loop when the button is remained pressed even very briefly, you are going to toggle at least 10's of times. However, if you detects the transition of the button from "not pressed" to "pressed", you will only have one per button press no matter how long you keep the button pressed. To detect button transition event, the code looks something like this:
Code:
bool button3IsPressed = false;
while (true)
{
    if (!button3IsPressed && joy1Btn(3))
    {
        //button 3 is pressed.
        button3IsPressed = true;
    }
    else if (button3IsPressed && !joy1Btn(3))
    {
        //button 3 is released.
        button3IsPressed = false;
    }
}

Now combining the two code:
Code:
int armPos = 0;
bool button3IsPressed = false;
while (true)
{
    if (!button3IsPressed && joy1Btn(3))
    {
        //button 3 is pressed.
        button3IsPressed = true;
        //toggle the arm position only when the button is first pressed.
        armPos = (armPos == 0)? 120: 0;
    }
    else if (button3IsPressed && !joy1Btn(3))
    {
        //button 3 is released.
        button3IsPressed = false;
    }
    servo[armServo] = armPos;
    wait1Msec(100);
}


Fri Dec 14, 2012 7:00 pm
Profile
Rookie

Joined: Sat Nov 17, 2012 10:27 pm
Posts: 5
Post Re: one button controlling help
Thanks for the help on the coding! I appreciate you telling me about how the processor runs through too quickly, but, if it is convenient, could you explain a little more on it? (It might help for future years and present problems.)

Thanks.


Sat Dec 15, 2012 11:57 am
Profile
Moderator
Moderator
User avatar

Joined: Tue Sep 14, 2010 9:19 pm
Posts: 496
Post Re: one button controlling help
Sure thing. Imagine that you have this code:
Code:
int i = 0;
while(true){
  if(button pressed){
    i = i + 1;
  }
}


What will the value of i if you hold down the button for 1 second? Well, I don't have the information to calculate it exactly right now, but it will a very, very big number.

Quote:
Technical info, ignore if you don't want to read it:

It would be be in the tens of millions. In fact, the integer variable is not big enough to hold this number (an integer on the NXT is only 2 bytes, which means you can only hold about 32,000 numbers in the positive direction), and it will overflow. This means it will wrap around, and start holding negative numbers. This will happen again and again, multiple times a second.


This is because the processor computes things very quickly (compared to us humans). For example, the main processor in the NXT runs at 48 Million cycles per second. This is why we have to make sure that the instruction only executes once when the button is pressed for the first time after being released. Does that clear things up, or am I still being unclear?

Aside: does anyone know about cycles per instruction for computation with ROBOTC? I know the NXT architecture does integer computation at 1 CPI, but how much overhead does ROBOTC create?

_________________
sudo rm -rf /


Sat Dec 15, 2012 1:15 pm
Profile
Guru
User avatar

Joined: Sun Nov 15, 2009 5:46 am
Posts: 1347
Post Re: one button controlling help
I believe RobotC is not running code natively. It's some sort of VM with byte code interpreter. So it is not as fast as running native code but still much faster than you can release your finger on the button. Therefore, don't count on reacting once when the button is pressed. It will for sure react multiple times a second.


Sat Dec 15, 2012 8:43 pm
Profile
Novice

Joined: Sun Oct 21, 2012 10:01 pm
Posts: 76
Post Re: one button controlling help
@MHTS: I recall reading somewhere that RobotC compiles to some sort of bytecode (sort of like C# or Java, I guess), rather than running native NXT machine code. That would also explain why I've never crashed a brick with a RobotC program.

I've never tried it in RobotC; what does writing outside the bounds of an array do, after all (I'm all too aware of the consequences in C++)?


Sat Dec 15, 2012 11:13 pm
Profile
Guru
User avatar

Joined: Sun Nov 15, 2009 5:46 am
Posts: 1347
Post Re: one button controlling help
amcerbu wrote:
@MHTS: That would also explain why I've never crashed a brick with a RobotC program.

No, it's still possible to crash RobotC. I have seen it a number of times :(


Sun Dec 16, 2012 12:56 am
Profile
Rookie

Joined: Tue Dec 11, 2012 9:19 pm
Posts: 21
Post Re: one button controlling help
MHTS wrote:
amcerbu wrote:
@MHTS: That would also explain why I've never crashed a brick with a RobotC program.

No, it's still possible to crash RobotC. I have seen it a number of times :(


Our brick crashed during autonomous in yesterday's competition.

Unfortunately, our drivers were not able to articulate the problem
well enough to convince the referee to reboot the brick in between
teleop and autonomous. That's been fixed. :)

When that happens it's equivalent to forfeiting a match. Was a bit
of a bummer because given the resulting score we very likely would have won
that match.


Sun Dec 16, 2012 4:29 pm
Profile
Rookie

Joined: Tue Dec 11, 2012 9:19 pm
Posts: 21
Post Re: one button controlling help
If you notice, the RobotC documentation will tell you that you
need to wait for a servo to reach a set position before moving
on in your code. This of course can create a problem if you
just use a wait function for a period of time as all other operations
will be locked out.

We use a separate task to avoid this problem.

e.g.
Code:
bool blockServo = false;
#define POS_1 0;
#define POS_2 120;
int targetPos = POS_1;

task waitForServo()
{
    blockServo = true;
    wait1Msec(1000);
    blockServo = false;
}

if(joy2Btn(1))
{
        if (blockServo) {
            return;
        }
       
        targetPos = (targetPos == POS_1) ? POS_2 : POS_1;

        servo[someServo] = targetPos;
        StartTask(waitForServo, kDefaultTaskPriority);
 }


Sun Dec 16, 2012 4:40 pm
Profile
Moderator
Moderator
User avatar

Joined: Tue Sep 14, 2010 9:19 pm
Posts: 496
Post Re: one button controlling help
skatefriday wrote:
If you notice, the RobotC documentation will tell you that you
need to wait for a servo to reach a set position before moving
on in your code.


Are you referring to the following line from the ROBOTC wiki?
Quote:
This function is used to set the position of the servos on a sensor port Servo controller. Values can range from 0 to 255. The firmware will automatically move the servo to this position over the next few update intervals. (Be sure to give the servo some amount of time to reach the new position before going on in your code.)


This simply reminds you that the servo does not move instantaneously. You can certainly set the servo value, and continue executing instructions in the same task without waiting for the servo to move to its assigned position (as long as those instructions do not depend on the servo being in the correct place). I just wanted to clear that up to avoid confusion, since your post seemed to imply that no more code could be executed in that task until the servo reached its position.

_________________
sudo rm -rf /


Sun Dec 16, 2012 11:12 pm
Profile
Rookie

Joined: Tue Dec 11, 2012 9:19 pm
Posts: 21
Post Re: one button controlling help
magicode wrote:
skatefriday wrote:
If you notice, the RobotC documentation will tell you that you
need to wait for a servo to reach a set position before moving
on in your code.


Are you referring to the following line from the ROBOTC wiki?
Quote:
This function is used to set the position of the servos on a sensor port Servo controller. Values can range from 0 to 255. The firmware will automatically move the servo to this position over the next few update intervals. (Be sure to give the servo some amount of time to reach the new position before going on in your code.)


This simply reminds you that the servo does not move instantaneously. You can certainly set the servo value, and continue executing instructions in the same task without waiting for the servo to move to its assigned position (as long as those instructions do not depend on the servo being in the correct place). I just wanted to clear that up to avoid confusion, since your post seemed to imply that no more code could be executed in that task until the servo reached its position.


Right. I was not trying to imply that at all. That would be
a gross misunderstanding of how the software works.

The discussion was in the context of how to respond when you are
in an event loop looking for button presses and the response to one
of those button presses is to move a servo. Noting that if a second
press of the same button moves the same servo that there will
be contention for servo position given that, without doing something
to lock out that button for a period of time.

The approach of using a second task to manage the semaphore that
locks out the servo move button during the portion of time the servo is
moving, but allows the event loop to continue processing other
user actions simply illustrates the point.


Mon Dec 17, 2012 5:39 pm
Profile
Guru
User avatar

Joined: Sun Nov 15, 2009 5:46 am
Posts: 1347
Post Re: one button controlling help
skatefriday wrote:
e.g.
Code:
bool blockServo = false;
#define POS_1 0;
#define POS_2 120;
int targetPos = POS_1;

task waitForServo()
{
    blockServo = true;
    wait1Msec(1000);
    blockServo = false;
}

if(joy2Btn(1))
{
        if (blockServo) {
            return;
        }
       
        targetPos = (targetPos == POS_1) ? POS_2 : POS_1;

        servo[someServo] = targetPos;
        StartTask(waitForServo, kDefaultTaskPriority);
 }

BTW, that's not a good reason to use task. Strictly speaking, that code does not resolve contention. By using task, you may introduce contention. From the description of your problem, why not implementing it like the following?
Code:
bool Btn1IsPressed = false;
int targetPos = POS_1;
while (true)
{
    if (!Btn1IsPressed && joy2Btn(1))
    {
        Btn1IsPressed = true;
        targetPos = (targetPos == POS_1) ? POS_2 : POS_1;
    }
    else if (BtnIsPressed && !joy2Btn(1))
    {
        Btn1IsPressed = false;
    }
    servo[someServo] = targetPos;
    wait1Msec(100);
}

Now the above code does not "wait for the previous servo move" to complete before toggling to the previous position. But in my opinion, that would be how I want it anyway. For example, if my robot arm is moving to the wrong position, I hate to wait for it to complete the move before I can correct it. If you insist that it should complete the previous move, you could do the following instead:
Code:
bool Btn1IsPressed = false;
bool servoMoveInProgress = false;
long servoFinishTime = 0;
int targetPos = POS_1;
while (true)
{
    if ((servoFinishTime > 0) && (nPgmTime > servoFinishTime))
    {
        //
        // Servo is done moving, let's indicate so.
        //
        servoFinishTime = 0;
    }

    if (!Btn1IsPressed && joy2Btn(1))
    {
        Btn1IsPressed = true;
        if (servoFinishTime == 0)
        {
            //
            // Only move the servo if it wasn't moving.
            //
            targetPos = (targetPos == POS_1) ? POS_2 : POS_1;
            servo[someServo] = targetPos;
            //
            // Indicate the servo is moving and wait 1 second for it to complete.
            //
            servoFinishTime = nPgmTime + 1000;
        }
    }
    else if (BtnIsPressed && !joy2Btn(1))
    {
        Btn1IsPressed = false;
    }
    wait1Msec(100);
}


Mon Dec 17, 2012 6:45 pm
Profile
Rookie

Joined: Tue Dec 11, 2012 9:19 pm
Posts: 21
Post Re: one button controlling help
MHTS wrote:
skatefriday wrote:
e.g.
Code:
bool blockServo = false;
#define POS_1 0;
#define POS_2 120;
int targetPos = POS_1;

task waitForServo()
{
    blockServo = true;
    wait1Msec(1000);
    blockServo = false;
}

if(joy2Btn(1))
{
        if (blockServo) {
            return;
        }
       
        targetPos = (targetPos == POS_1) ? POS_2 : POS_1;

        servo[someServo] = targetPos;
        StartTask(waitForServo, kDefaultTaskPriority);
 }

BTW, that's not a good reason to use task. Strictly speaking, that code does not resolve contention. By using task, you may introduce contention. From the description of your problem, why not implementing it like the following?
Code:
bool Btn1IsPressed = false;
int targetPos = POS_1;
while (true)
{
    if (!Btn1IsPressed && joy2Btn(1))
    {
        Btn1IsPressed = true;
        targetPos = (targetPos == POS_1) ? POS_2 : POS_1;
    }
    else if (BtnIsPressed && !joy2Btn(1))
    {
        Btn1IsPressed = false;
    }
    servo[someServo] = targetPos;
    wait1Msec(100);
}

Now the above code does not "wait for the previous servo move" to complete before toggling to the previous position. But in my opinion, that would be how I want it anyway. For example, if my robot arm is moving to the wrong position, I hate to wait for it to complete the move before I can correct it. If you insist that it should complete the previous move, you could do the following instead:
Code:
bool Btn1IsPressed = false;
bool servoMoveInProgress = false;
long servoFinishTime = 0;
int targetPos = POS_1;
while (true)
{
    if ((servoFinishTime > 0) && (nPgmTime > servoFinishTime))
    {
        //
        // Servo is done moving, let's indicate so.
        //
        servoFinishTime = 0;
    }

    if (!Btn1IsPressed && joy2Btn(1))
    {
        Btn1IsPressed = true;
        if (servoFinishTime == 0)
        {
            //
            // Only move the servo if it wasn't moving.
            //
            targetPos = (targetPos == POS_1) ? POS_2 : POS_1;
            servo[someServo] = targetPos;
            //
            // Indicate the servo is moving and wait 1 second for it to complete.
            //
            servoFinishTime = nPgmTime + 1000;
        }
    }
    else if (BtnIsPressed && !joy2Btn(1))
    {
        Btn1IsPressed = false;
    }
    wait1Msec(100);
}


I guess it depends upon how you define contention. I was defining it
the way that I thought the original poster was. That is that if you
expect a button press to move a servo to a particular location and
that button press is being monitored in an event loop and that button
is also used to move the servo to a second position, toggling between
the positions, then contention is defined as the tendency of the button
press to appear as a single press to the human, but in fact is read as
still pressed for a period of time within the event loop which causes the
servo to toggle back and forth fast enough to appear "hesitant" or twitchy.

The task rather cleanly solves the problem.


Mon Dec 17, 2012 9:04 pm
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 14 posts ] 

Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  



Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.