Common Coding Mistakes Found At VEX Worlds 2022

Hey Everyone!

During worlds, the software team and other VEX developers do a lot of the software support for teams. So if they have any issues we dig in to it to find if it is a mistake with the code or a potantial hardware issue. One of the other VEX developers did a write up in the VEX Forum about some of the common issues that students were running in to at Worlds.

Hopefully this can help your students avoid these issues as well!

First topic is incorrect use of event handlers, something we have covered before but it seems everyone forgets.

The symptom is that, for example, an event handler on a controller button sometimes works and sometimes doesn’t. At worlds the most common use was for firing pneumatics, a button push to set one state and a second button push to revert back to original state.

Simple code for a callback could look as follows.

void button_callback() {
    static  int calls = 0;
    Brain.Screen.setFont( mono40 );

    Brain.Screen.printAt( 100, 160, "Button %4d", ++calls );
}

This is just for demonstration purposes and prints the number of callbacks on the brain screen.

We often saw event registration at the beginning of driver control like this.

void usercontrol( void ) {
    Controller.ButtonA.pressed( button_callback );


    // more code
}

So what’s the problem with that ?

usercontrol can be called more than once.

The reason is that if the controller is connected (radio linked) to the robot and the field control is not connected (either new or legacy) then usercontrol will run. This is expected and what we usually want to happen when practicing. Once the field control is connected then the robot will usually be disabled unless the match has already started. When the driver control period starts then usercontrol will run again and the button_callback registered a second time, when controller button A is pressed button_callback will be called twice (and potentially more if field control was connected and disconnected more than once) and obviously if it is toggling a pneumatic solenoid nothing will actually happen (the solenoid will be enable and disabled very quickly or not at all).

Event registration should only happen once at the beginning of main. This would be the correct place for the code.

int main() {    
    Controller.ButtonA.pressed( button_callback );

    Competition.autonomous( autonomous );
    Competition.drivercontrol( usercontrol );

    pre_auton();

    while(1) {
        this_thread::sleep_for(10);
    }
}

The order that teams connected to the field and started their code varied and they had inconsistent results because of registering events in usercontrol.

Second topic, incorrect use of local variables.

Consider this code, a very simplified version of something we saw at worlds.

code

vex::pneumatics   pn(Brain.ThreeWirePort.A );

void usercontrol( void ) {
    int my_state = 0;

    while (true) {
      if( Controller.ButtonA.pressing() ) {
        if( my_state == 0 ) {
          my_state = 1;
        }
        else
        if( my_state == 1 ) {
          my_state = 0;
        }

        if( my_state == 1 ) {
          pn.open();
          Brain.Screen.printAt( 100, 100, "Open   " );
        }
        if( my_state == 0 ) {
          pn.close();
          Brain.Screen.printAt( 100, 100, "Closed " );
        }

        while( Controller.ButtonA.pressing() ) {
          this_thread::sleep_for(10);
        }
      }   

      this_thread::sleep_for(10);
    }
}

The idea is to toggle pneumatics open and closed (much more was involved in the student code)

The team may allow usercontrol to run before connecting field control and press button A to open the pneumatics, then connect field control. The first time usercontrol is run in the match the pneumatics do not close when the button is pressed. The mistake is assuming that a local variable will maintain state between calls to usercontrol, the solution for this is to make the variable my_state static or (worse) global. my_state then retains its value between calls.

static int my_state = 0;

And finally, motor setVelocity is evil…

Consider this code.

void autonomous( void ) {
    m1.setVelocity( 100, percent );

    this_thread::sleep_for(1000);

    m1.spin( reverse );
} 

/*----------------------------------------------------------------------------*/

void usercontrol( void ) {
    m1.setVelocity( 0, percent );
    m1.spin( forward );

    while (true) {
      m1.setVelocity( Controller.Axis1.position(), percent );

      this_thread::sleep_for(10);
    }
}

The intent is for the motor to spin at 100 percent in reverse during autonomous after something else has happened. In reality the motor will often spin forwards at 100% immediately and then spin in reverse.

The problem is again that usercontrol can often run when the controller/field connection order is inconsistent. If user control runs, the motor is told to spin at 0 velocity and then we run into problems.

setVelocity has different functionality depending on whether the motor is stopped, when it means set the velocity to be used by the next spin command (with no velocity parameter), however, if the motor has already been told to spin then it means change velocity to a new value.

When auton runs after usercontrol and setVelocity used, the motor will move.

As I said, setVelocity is evil.

but the pattern comes from blocks where it was required to reduce the number of parameters each block had and overloads were not available.

You could stop the motor at the beginning of auton, but my preferred solution is to never use setVelocity and add velocities as parameters to the spin command.

void autonomous( void ) {
    this_thread::sleep_for(1000);

    m1.spin( reverse, 100, percent );
} 

/*----------------------------------------------------------------------------*/

void usercontrol( void ) {
    while (true) {
      m1.spin( forward, Controller.Axis1.position(), percent );

      this_thread::sleep_for(10);
    }
}
2 Likes

This is really helpful to see the best places to put user control code.

The setVelocity issue you raise gets me thinking about the way I had my students learn to do some of this in VEX VR this year while we were virtual first semester. The structure there needed to to use set_drive_velocity and drive separately because there wasn’t an option in Python. Is the velocity parameter for spin only included in the C++ version?

I taught students some control algorithms using VR and this was how I did it:


SETPOINT = 400
k = 0.5
def main():
    brain.timer_reset()
    while(brain.timer_time(SECONDS) < 3.0):
        error = SETPOINT - location.position(Y,MM) 
        
        output = k*error
        if(output >= 100):
            output = 100
        elif(output < -100):
            output = -100
        
        drivetrain.drive(FORWARD)
        drivetrain.set_drive_velocity(output,PERCENT)
        wait(1,MSEC)
    drivetrain.stop()

# VR threads — Do not delete
vr_thread(main)

Is this a limitation of VR or is there a difference in doing the same thing with Python? My v5 is at school so I can’t check at the moment. :slight_smile:

Thank you for sharing these tips from what you saw at Worlds.

1 Like

Those are great questions!

So firstly, this isn’t a limitation of Python. If you’re using Python on a V5 you have access to the same functionalities as C++, with just the twists of using the Python language.

For example, if you wanted to do a spinFor with a motor in python and pass the percent velocity it would look like this:

motor_1.spin_for(FORWARD, 360, DEGREES, velocity=75)

The reason the examples given were in C++ was because that’s what most competition teams use, but Python will have the same functionality on the robot and has very similar control functions.

As far as for VR, we don’t have the velocity as an arugument because we were primarily focusing on the blocks programming when we initially developed VR, so when we added Python we used the same command interfaces. Also, since the VR robot is ultimately a fully virtual robot, there won’t be the same pitfalls as mentioned since a lot of the issues seen during world were on physical robots running in competition. So calling the set_velocity like in your control code is completely valid in VR.

Though, it could be a good idea to introduce these ideas early on so students can avoid these pittfalls if they end up doing competitions. So stuff like this could be worth adding to VR and the Virtual Skills platforms. Definitely something I’ll discuss with the other developers!