Programming the Joystick

I am trying to implement a code that will use a single joystick. Last year, my team based our programming on a sample program provided by Robot C:

task main ()
{

  while(1 == 1)
  {
    motor[leftMotor]  = (vexRT[Ch2] + vexRT[Ch1])/2;  // (y + x)/2
    motor[rightMotor] = (vexRT[Ch2] - vexRT[Ch1])/2;  // (y - x)/2
  }
}

How does adding, subtracting, and then dividing the values of the channels work?

You can treat the joystick channels as any other number. This code:


vexRT[Ch3]

can be treated as any regular number; it just changes in value from -127 to 127 depending on the position of the joystick.

Sorry, let me be more specific…

What is the difference between adding and subtracting the values of the channels for the left and right motor? When going in forwards, the value for Ch2 (y) is positive; therefore, if Ch2 = 100 and Ch1 = 5

Why do you have to subtract the values for the right motor? Why can’t it be the same as the left motor?

This code is for the arcade style of driving, where one joystick controls the chassis movement of the robot. The back and forth motion of the robot is controlled by the y axis of the joystick, and the turning of the robot is controlled by the x axis of the joystick. To see why the code provided achieves this behavior, plug in values to test the output.
If y is 127, and x is 0, then both motors go forward.
If y is 127, and x is 127, then the left motor goes forward while the right motor stays still.
If y is 127, and x is -127, then the right motor goes forward while the left motor stays still.
You can fill in the rest of the table using the values 127, 0, and -127 on each of the axis to see what would happen.

If you have the same equation for both motors, then lets look at what would happen:
If y is 127, and x is 0, then both motors go forward.
If y is 127, and x is 127, then both motors go forward.
If y is 127, and x is -127, then both motors stay still.
Again, you can fill in the rest of the table.

Since the motors are on different sides of the robot, you want them doing different things sometimes so that you can turn. the robot.

Ok got it.

For my “lynfield drive”, I want to be able to control the left joystick for forwards/backwards and the right joystick for horizontal/spin. Here is part of my code:

 
int joy_x;
  int joy_y;
  int joy_threshold = 10;

  while(true)
  {
    joy_x = vexRT[Ch1];
    joy_y = vexRT[Ch3];

/*+++++++++++++++++++ Drive Code +++++++++++++++++++*/

    // Forward
    if((abs(joy_y) > joy_threshold) && (joy_y > 0))
    {
      motor[leftDrive]  = (joy_y);
      motor[rightDrive] = (joy_y);
    }

    // Backwards
    else if((abs(joy_y) > joy_threshold) && (joy_y < 0))
    {
      motor[leftDrive]  = (joy_y);
      motor[rightDrive] = (joy_y);
    }

    //Right Movement and Spin
    else if((abs(joy_x) > joy_threshold) && (joy_x > 0))
    {
      motor[backDriveL] = (joy_x);
      motor[backDriveR] = (joy_x);
      motor[leftDrive] = (joy_x);
      motor[rightDrive] = -1*(joy_x);
    }

    //Left Movement and Spin
    else if((abs(joy_x) > joy_threshold) && (joy_x < 0))
    {
      motor[backDriveL] = (joy_x);
      motor[backDriveR] = (joy_x);
      motor[leftDrive] = -1*(joy_x);
      motor[rightDrive] = (joy_x);
    }

    //Else - Stop Motors
    else
    {
      motor[leftDrive] = 0;
      motor[rightDrive] = 0;
      motor[backDriveL] = 0;
      motor[backDriveR] = 0;
    }

Will this work? Is it efficient? How can I make the robot move like the traditional arcade style but without the spin (Use the left joystick for x, y, and z movement) and when I want to spin in place use the right joystick?

Depending on how you have your motors flipped, the code that you posted should allow you to drive your robot. However, since everything is in “if else” form, you cannot do more than one behavior at once. For example, if you move forward, moving the right joystick will do nothing as long as the left joystick is about the threshold. It will make your life much easier, and programming faster, if you map each wheel to a mathematical function of joysticks just like the example code above. I can be done, just think about how the joystick relates to each wheel. As for the holonomic arcade drive, I don’t understand what you mean by the z-axis. Is your robot moving up and down? Anyway, this can be done with the same method of mapping joystick arithmetic to each wheel. I don’t want to post “here’s how you do it” code, because nobody learns anything from that. Just try and think about how you want each wheel to react when you move the joysticks. So ask yourself, if the robot is on the ground, and you move the left joystick up, what do you want each wheel to do? What about if you move the left joystick sideways? What if you move it up and sideways. Answering these questions will allow you to add and subtract the correct joystick axis to get the desired result. Good luck.

lynfield drive maps to standard Plus format holonomic.
Look for holo to find examples.

Sounds like you want vertical joystick for both sides forward/back.
horizontal joystick for slide drive left, right
So far, that sounds like orthographic drive.
and right joystick (vert or horizontal?) for spinning the sides in opposite directions.

You shouldn’t need any if/then/else; just a math function for each motor as a function of some or all of the joystick values.

Ok so my robot has evolved from a “lynfield drive” to a mecanum drive. We are now in the process of programming. This is drive code:

This is just a preliminary code, we will later add thresholds to improve it. Let’s say that the left joystick is at the top right corner: Channel 3 = 127, Channel 4 = 127, and Channel 1 = 0; motor[frontLeft] would = 200. What will the motor do when you add them, does Robot C automatically = the value to 127 if it’s greater than 127? Do I have to add the check?

If you send a value greater than 127 to the motor it will run at maximum speed. RobotC will limit to 127, however, it’s IMHO good practice to limit in the code.

For example, I may write instead of
motor[frontRight] = vexRT[Ch3] - vexRT[Ch1] - vexRT[Ch4];

#define MAX_MOTOR_SPEED 127
#define MIN_MOTOR_SPEED (-127)

...

int motorFR;

motorFR = vexRT[Ch3] - vexRT[Ch1] - vexRT[Ch4];

if( motorFR > MAX_MOTOR_SPEED ) motorFR = MAX_MOTOR_SPEED;
if( motorFR < MIN_MOTOR_SPEED ) motorFR = MIN_MOTOR_SPEED;

motor[frontRight] = motorFR;

Thank you. “#define MIN_MOTOR_SPEED 127”, is it suppose to be -127?

Yes, oops, I was typing too fast and was interrupted.

Getting the precise encoder value for each wheel is going to be a pain -.-.

Perhaps this would make a difference if you are going to port the code to a different platform, or programming language, but doesn’t this just add unnecessary complication and computation when essentially the same code is already running int the background?

Well, it is just an opinion. Every programmer is free to make their own choices. We see the results of how RobotC handles values that are out of bounds today, but don’t really know what they are doing. To me it’s no different than checking for a NULL pointer of bounds checking an array index. Sometimes a large amount of code is dedicated to checking for errors that should not happen but may happen.

You are right, it’s probably overkill but the cortex is not exactly underpowered and there’s no other downside.

You could also reduce the number of lines of code needed by using a more complex if statement like this that tests both ends of the speed range at once.

#define MOTOR_SPEED_LIMIT 127

...

if( abs(motorFR) > MOTOR_SPEED_LIMIT ) motorFR = sgn(motorFR) * MOTOR_SPEED_LIMIT;

or if you want to be really fancy, use a macro.

#define MOTOR_SPEED_LIMIT 127
#define LIMIT_MOTOR_SPEED(x) do {\
                if( abs(x) > MOTOR_SPEED_LIMIT ) x = sgn(x) * MOTOR_SPEED_LIMIT;\
                }while(0)

...

LIMIT_MOTOR_SPEED(motorFR);

Lynfield (aka H aka slide) drive is already a holonomic drive, so not much evolution was necessary.

Why?

I’ve seen some Holonomic code that finds the maxv = max(abs(motor target values)) and then divides all the motor values by 127/maxv, so they are all in correct proportion while limiting to max 127.

That makes more sense (to me) if the joystick is providing target motor speeds,
and each motor is PIDspeed controlled to the that target.

One of the practical problems with holonomic drive is getting autonomous routines to work well.
The simple approach (easiest for Lynfield and other + type holo) is to run only 1 axis (X,Y,theta) axis at a time.
The cooler approach (with full encoders) is to create a table of motor drives by recording a driving pattern (or just record the encoders while hand-dragging your robot), then replay it.
This might be refered to as “etch-a-sketch drive”, unless I’m confusing that term with some other idea. I’m not sure anyone has it working. Some teams have moved beyond, or skipped over this step of dead-reckoning, in favor of field feedback (line following, line crossing, ultra-sonic wall fallowing, goal bumping, etc).

Yes, I believe Team 1166 had a system by which they could drive there robot while it was recording encoder information, then have it dump that data to the computer so they could put it in their program, and then have it reproduce those same steps during Autonomous.