C++ Methods? Tasks? Slew Rate

I’m trying to make a slew rate function since the robot is really jerky in the auton if we go 100% power. The function itself compiled and when we put it to test, it did one wheel at a time. its because it goes through each motor separately. How do I make a non-blocking function because tasks don’t seem to work, it gives me an error for some reason. I don’t know if I should make a class or a method for slew rate, but I really want to implement it into our bot.

Here’s the code:


double wheel_diameterCM = 10.16;
double circumfrence = wheel_diameterCM * M_PI;

//Idk if its the best way to do this, but i dont know how to make a method or a nonblocking function in C++

int slew_rate(vex::motor MotorName, int end_power, int time, double dist_to_destinationCM, bool rev){

    MotorName.resetRotation();
    double deg_to_rotate = (360 * dist_to_destinationCM) / circumfrence;  //277
    
    if (rev == false){

        while (deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

            int power = 5;
            MotorName.setVelocity(0,vex::percentUnits::pct);
            MotorName.spin(vex::directionType::fwd);

            while (power < (end_power - 5) && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                MotorName.setVelocity(power,vex::percentUnits::pct);
                power += 5;
                vex::task::sleep(time);
            } 

            if (MotorName.rotation(vex::rotationUnits::deg) > deg_to_rotate - circumfrence){

                while (power > 0 && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                MotorName.setVelocity(power,vex::percentUnits::pct);
                power -= 10;
                vex::task::sleep(100);
                }
            }

            MotorName.stop();
        }
    }else if (rev == true){
    
        while (deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){
        
            int power = 5;
            MotorName.setVelocity(power,vex::percentUnits::pct);
            MotorName.spin(vex::directionType::rev);

            while (power < end_power && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                MotorName.setVelocity(power,vex::percentUnits::pct);
                power += 5;
                vex::task::sleep(time);
            } 

            if (MotorName.rotation(vex::rotationUnits::deg) > deg_to_rotate - circumfrence){

                while (power > 0 && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                    MotorName.setVelocity(power,vex::percentUnits::pct);
                    power -= 10;
                    vex::task::sleep(100);
                }
            }

            MotorName.stop();
        } 
    }
    
    return 0;
}


///////this is the function part////////////

void move_slew_fwd (int time, double dist, bool rev){
    
    vex::task t1 (slew_rate(FrontRight, 100,time, dist, false));
    vex::task t2 (slew_rate(FrontLeft, 100,time, dist, false));
    vex::task t3 (slew_rate(RearRight, 100,time, dist, false));
    slew_rate(RearLeft, 100, time, dist, false)                               // I tried to make a nonBlock here but it gives me errors in the tasks
    
}


Thank you!

you cannot pass parameters to tasks

Ya just to add to this, the common way to have a task do slew rate control is have a global variable “goal power” for each motor (easiest to do with an array). The task compares each motor’s current power to its goal power and increments/decrements the current power as needed to reach the goal power.

While not fundamentally disagreeing, can’t you essentially pass parameters to tasks? Build a function to be used as a task as a method within a custom class. You can then launch difference instances of what is essentially the same task but is operating only with a given object’s values. By passing values to that object and using its method, the task then receives those values. I know it’s not really the same as passing parameters to tasks, but it should be effectively pretty similar, right?

I get that it doesn’t fix the problem above. It’s more of a suggestion about a different approach.

not sure I completely understand what you are proposing, but instance methods cannot be used as tasks either, only static methods.

No, you understood me. That’s a shame. The arguments I’ve seen against wanting to send parameters to tasks have also mostly been against wanting to send parameters to functions, yet we all know it’s handy to send parameters to functions.

We don’t have quite the same limitlessness of a typical program with our restrictions on motors and the like, but the above code at least suggests the value in being able to pass parameters to tasks. I think C# specifically allows passing parameters to tasks, though I could easily be mistaken on that, too.

You can still manage it. It just has to be round about enough to be a pain. Read in the values when the task is created and only use them internally, being sure no other task is waiting to read them, too. To do that you really just need to check on a lock before moving on from creating a prior task (not before starting a new one, but when creating the prior one). That way you know a prior task has accepted its values and you’re free to stick new values into the global variable(s) or whatever.

For the OP, to avoid making four copies of the same function, you could create for() loops or similar inside it. Each time you’re setting values, do it four times, once for each motor. If most the values are being set the same way, you won’t have so many global variables to deal with. But if you have a similar things for turning, maybe an array of structs or similar could consolidate the code.

@callen Is there a way to make a nonblocking function for motors without using the ones given to us? I’m very confused on why you can’t pass things into tasks because in this case, wouldn’t it make sense? Like everyone who wrote above is like a pro programmer and I’m just trying to learn this way of coding.

Well, that’s what I’m trying to do but how would I make an array for that? Also, the problem is that I can’t run all motors at the same time, that’s why I’m trying to make a task for it but I don’t want 20 different ints with different settings just to go forwards, which is why I want to pass them into tasks.

For the last part of the quote, would I be able to run them at the same time?

Also, how do I get around the problem where the motors don’t always go up to 100% power in this case?
And is VEX C++ Pro like normal C++?

I’m sorry if I don’t understand some of the stuff you guys say, but I’m trying my best to interpret it.

Ive got this so far, but the only probllem, i sstill need to pass the values in somehow, they cant be constant unless i make 20 different arrays.


typedef struct motorSetting{
    
    vex::motor MotorName;
    int end_power;
    int time;
    double dist_to_destinationCM;
    bool rev;
    
} rate;



rate motors] = {
    
    { FrontRight, 100, 50, 72, false},
    { FrontLeft, 100, 50, 72, false},
    { RearRight, 100, 50, 72, false},
    { RearLeft, 100, 50, 72, false},
};

I don’t even know if what I’m doing is the correct way to approach this.

As a basic, general computer science reason, no there is no reason you can’t pass parameters into tasks. But if you look at tasks in C and C++, you cannot do it. I’m not a professional programmer by any stretch, and I’m far more used to C than C++. I’d been hoping methods would provide the bypass I desired, but they don’t. I suspect the actual reason is historical more than anything else. A lot of times it takes realizing that something will be useful before it gets implemented, though there are certainly times when . For a major look at that, consider object-oriented programming (like C++) v. programming like in C.

There are some really complex ways to do it in C that I looked at but can’t quite remember, something with null pointers and/or lambda expressions if I remember correctly. I’m not sure if they can be implemented the same way in C++. I’m sure @jpearman could speak to this more; he knows far more than I do. But this is beyond the level you want right now. So let’s skip forward to solving your dilemma.

I see multiple comments about running motors at the same time. Generally, if they’re not running at the same time, it’s because you’re using a method that has an optional waitForCompletion boolean in it and haven’t set that to false. Since it’s true by default, that will mean one motor will run until it’s done and then the next thing will happen.

Doing things with tasks is fine, but it isn’t necessary. In this case, a single task would suit what you’re doing nicely if you want to use tasks, and there is no reason not to. What you might do to keep the same framework you’re working with is to either define a bunch of global variables or an array, doesn’t really matter too much in the end. Use functions to change those values. So move_slew_fwd would change the appropriate values. Meanwhile you’d have an eternally running slew rate task that handles the slew rates for all the motors much as you’re doing it. You could then make other functions like spin_turn_slew_right that just set the values differently. When those values change, the slew rate control task will see the change and make the robot respond appropriately. Your code later on will look the same that way. I think this would work well for you as it’s just a variant to the style you’re already doing. I don’t have time to write sample code, but I could try to restate what I just said in this paragraph if it’s isn’t clear.

First, I assume you’re not talking about power since you seem to be looking at speed, so I’m going to assume you’re talking about speed. That could be a whole bunch of things and might have nothing to do with code. Have you checked friction and the like?

Yes, with some libraries added by default.

Yes, work with this. Make sure this is globally defined so your slew rate control task can see it. Make sure your single task is always reading this array. Make functions whose sole purpose is to change the values in this array. When the slew rate control task sees the values changing, it will respond. That also means you may want a specific function to stop the motors, setting all appropriate values to 0.

@callen

So I’m thinking of doing something like this:

But my int cycle isn’t reading the stuff in the array, how do I pass that in?


double wheel_diameterCM = 10.16;
double circumfrence = wheel_diameterCM * M_PI;



typedef struct motorSetting{
    
    vex::motor MotorName;
    int end_power;
    int time;
    double dist_to_destinationCM;
    bool rev;
    
} rate;



rate motors] = {
    
    { FrontRight, 100, 50, 72, false},
    { FrontLeft, 100, 50, 72, false},
    { RearRight, 100, 50, 72, false},
    { RearLeft, 100, 50, 72, false},
};

int cycle(){
    
    MotorName.resetRotation();
    double deg_to_rotate = (360 * dist_to_destinationCM) / circumfrence;  //277

    if (rev == false){

        while (deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

            int power = 5;
            MotorName.setVelocity(0,vex::percentUnits::pct);
            MotorName.spin(vex::directionType::fwd);

            while (power < (end_power - 5) && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                MotorName.setVelocity(power,vex::percentUnits::pct);
                power += 5;
                vex::task::sleep(time);
            } 

            if (MotorName.rotation(vex::rotationUnits::deg) > deg_to_rotate - circumfrence){

                while (power > 0 && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                    MotorName.setVelocity(power,vex::percentUnits::pct);
                    power -= 10;
                    vex::task::sleep(100);
                }
            }

            MotorName.stop();
        }
    }else if (rev == true){

        while (deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

            int power = 5;
            MotorName.setVelocity(power,vex::percentUnits::pct);
            MotorName.spin(vex::directionType::rev);

            while (power < end_power && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                MotorName.setVelocity(power,vex::percentUnits::pct);
                power += 5;
                vex::task::sleep(time);
            } 

            if (MotorName.rotation(vex::rotationUnits::deg) > deg_to_rotate - circumfrence){

                while (power > 0 && deg_to_rotate > MotorName.rotation(vex::rotationUnits::deg)){

                    MotorName.setVelocity(power,vex::percentUnits::pct);
                    power -= 10;
                    vex::task::sleep(100);
                }
            }

            MotorName.stop();
        } 
    }
}

void slew_rate(){
    
    for (int i = 0; i <= motors.size; i++ ){

       vex::task t1 (cycle);
    }
}

Anyone else read this and picture Gandalf on the bridge stating “You shall not pass.” :slight_smile:

@Doug Moyers Lol

@callen Thank you sm for your help, my friend helped me get a different approach to the problem.

haha

with some classes like std::thread you can, but it gets more complicated with an embedded system running a relatively lightweight scheduler. I have a plan for the future to allow an instance ptr to be passed into a task, we already do that with some of the event handling system, then (using event as an example) you can start to do things like this (ignore that actual code here, I just pulled something out of the sdk)

    event::init( _index, (1 << static_cast<uint32_t>(tEventType::EVENT_DISABLE   )), _disable       , static_cast<void *>(this));

then when the event handler is called you can get the instance back


void
competition::_disable(void *arg) {
      if( arg != NULL) {
        competition *instance = static_cast<competition *>(arg);
etc........

but it can get complicated really fast, and we are trying to keep everything relatively simple at this point.

In terms of how I might approach this using C++, I would probably subclass motor to add additional variables needed to handle slew control.

  
namespace vex {
  class slew_motor : public vex::motor {
    friend class smart_motors;

    private:
      short   motor_cmd;
      short   motor_req;
      short   motor_slew;
      
    public:
       slew_motor( int index ) : motor( index ) {
         motor_slew = 10;
         motor_cmd  = 0;
         motor_req  = 0;
       };      
      ~slew_motor() {};

      // Allow overloading of base class methods
      using motor::spin;

      void spin( directionType dir, double velocity ) {
        if( dir == directionType::rev )
          velocity = -velocity;

        motor_req  = velocity;
      }
      
      short req() {
        return motor_req;
      }
      short cmd() {
        return motor_cmd;
      }      
  };
}

storing needed variables and adding a new method spin as an overload.

then have a class to collect several instances of these together, bearing in mind some of this has to be static so we can use as a task.


namespace vex {
  class smart_motors  {
    private:

       void _addMotor() {};
       template <typename... Args>
       void _addMotor(  vex::slew_motor &m1, Args &... m2 ) {
           _motors.push_back(std::reference_wrapper<slew_motor>(m1));
           _addMotor( m2... );
       }

    public:
      static std::vector<std::reference_wrapper<vex::slew_motor>> _motors;

      static int slew_task() {
          int motorTmp;
          
          while(true) {
            for ( auto mw: _motors) {
              slew_motor &m = mw.get();
              
              // if 0 then we skip, motor is disabled from control
              if( m.motor_slew == 0 )
                  continue;

              // get the last velocity requested.
              motorTmp = m.motor_cmd;
              
              // Do we need to change the motor value ?
              if( motorTmp != m.motor_req ) {
                  // increasing motor value
                  if( m.motor_req > motorTmp ) {
                    motorTmp += m.motor_slew;
                    // limit
                    if( motorTmp > m.motor_req )
                        motorTmp = m.motor_req;
                    }

                  // increasing motor value
                  if( m.motor_req < motorTmp ) {
                    motorTmp -= m.motor_slew;
                    // limit
                    if( motorTmp < m.motor_req )
                        motorTmp = m.motor_req;
                    }

                  // finally set motor
                  m.motor_cmd = motorTmp;
                  m.spin( directionType::fwd, motorTmp, velocityUnits::rpm );
                }
            }
            
            this_thread::sleep_for(20);
          }
          return(0);
       }
      
       smart_motors() {};
      
       template <typename... Args>
       smart_motors(  vex::slew_motor &m1, Args &... m2 ) {
          _motors.push_back( std::reference_wrapper<slew_motor>(m1) );
          _addMotor( m2... );
          vex::thread t( slew_task );
       }
      ~smart_motors() {};
  };
  
std::vector< std::reference_wrapper<vex::slew_motor>> smart_motors::_motors;
}

we are using some intermediate level C++ here, I’m not going to explain it all and I’m not expecting students to copy and use this, just wanted to show what’s possible.

anyway, then motors and the smart motor controller are instantiated like this.

slew_motor m1( vex::PORT1 );
slew_motor m2( vex::PORT2 );
slew_motor m3( vex::PORT3 );

smart_motors s( m1, m2, m3 );

Here we have three “slew_motor” instances that are then collected together using the “smart_motors” controller class.

just call the new spin method to run them

void abutton() {
  m1.spin( fwd, 100 );
}
void bbutton() {
  m1.spin( fwd, -100 );
}
void ybutton() {
  m1.spin( fwd, 0 );
}

int main() {
  Controller.ButtonA.pressed( abutton );
  Controller.ButtonB.pressed( bbutton );
  Controller.ButtonY.pressed( ybutton );

  while(1) {
    // show info for first motor
    Brain.Screen.printAt( 10, 50, "val %4d %4d", m1.req(), m1.cmd() );
    // Allow other tasks to run
    this_thread::sleep_for(10);
  }
}
1 Like