Preseason Lesson4: Difference between revisions

From 1511Wookiee
Jump to navigationJump to search
No edit summary
No edit summary
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><div class="mw-parser-output"><span style="font-size: large;">'''Change motor speed using joystick'''</span></div> <div class="mw-parser-output">
= Introduction =
The goal is to make the motor's speed change based on the position of the joystick. When the joystick is all the way forward, the motor will move at full forward speed. When the joystick is all the way back, the motor will move at full reverse speed.


&nbsp;
This lesson will be split in two parts.  Complete and test out part 1 before moving to part 2!


It is expected that you completed [http://penfieldrobotics.com/wiki/Preseason%20Lesson3 <font color="#0066cc">Lesson 3</font>] and have the code available.
Overall, this lesson will use most of what you have already learned to do similar, but still different, things. Our previous lessons have focused on control that mimics some simple appendages.  This lesson will focus on control that might be more appropriate for a drive system.


&nbsp;
We will be using a different motor and controller for this lesson and all new code you write will not directly interact with your existing code from previous lessons (as in, you won't be changing your existing code, just adding new/additional things to it).


Link to the Joystick documentation: [https://first.wpi.edu/FRC/roborio/release/docs/cpp/classfrc_1_1Joystick.html <font color="#0066cc">Joystick</font>]
Before starting this lesson, you should have completed the prior 3 lessons.  Start with your code from the end of [[Preseason Lesson3|Lesson 3]]


&nbsp;
= Part 1: Control Speed of Motor =


You will not need to make any changes to&nbsp;Robot.h.
In this part, you will use the Y-axis of a new Joystick to directly control the speed of a new motor.  We will use a dead-band as we did in prior lessons to prevent accidental driving of the motor from small movement of the joystick.


&nbsp;
== Part 1 Detailed Goals ==
* Use the Y-axis of the new Joystick on the driver station to control a new motor
* If the stick is moved forward, the motor should go forward.  If moved down/toward you, it should drive the motor backward.
* The motor to control is the one immediately to the right of the one you have been using until now. 
** This motor is also controlled by a TalonSRX motor controller
** You will need to look at the labels on the test board to know what the motor controller's ID/address number is
* Use a deadband of +/- 1/4 of the Y axis range (so, motor should not move unless the stick is moved more than 1/4 of the travel)
* The speed of the motor should directly match the amount that the stick is moved


In Robot.cpp, you will want to get the joystick position and store it in a variable. The variable can be declared at the top of&nbsp;the Robot::OperatorControl() method (i.e. the first line). Refer to the joystick documentation to&nbsp;see what data type GetY() returns. You will call GetY() and save what it returned into your varaible.
== Part 1 Guided Help ==


Let's also adjust the dead band (the area in which the joystick has no affect) to be -0.25 to +0.25.
This lesson will have significantly less help than prior ones.  Most of what you need to do is using things you have already done!  Here are some ordered hints to get you there:
# You will need to make a new Joystick object to represent the new Joystick.  Look at the driver station to get the address/ID number!
# You will need to make a new motor controller (TalonSRX) object to control the new motor.  The ID of the new motor's controller is labelled on it on the test board.
# In prior lessons you made a deadband of 1/3rd the motion of the controller.  Remember that for this lesson, we are using 1/4 the range of motion of the joystick for the deadband!
# Once you have handled the deadband, the speed of the motor can be simply set to the value read from the Joystick Y axis when you are outside of the deadband.


    if (joystick trigger is pressed) {
== Part 1 Test ==
        <span style="color: rgb(231, 76, 60);">assign your variable the joystick position</span>
 
        if ((<span style="color: rgb(231, 76, 60);">your variable is less than -0.25</span>) and (switch A is not tripped)) {
As always, coordinate with your teammates to use the test board to test.  Test the following:
            spin motor forward<span style="color: rgb(231, 76, 60);"> the same speed as the joystick</span>
# The new motor should not move if the joystick is left stationary.
        }
# Move the joystick a small amount off center.  The motor should not move!
        else if ((<span style="color: rgb(231, 76, 60);">your variable is greater than 0.25</span>) and (switch B is not tripped)) {
# Move the joystick forward more than 1/4 off center, slowly, toward fully forward.  You should see the motor move slowly once you pass 1/4, then speed up and be going full speed when all the way forward.  The LED on the motor controller should be flashing green while moving until you are fully forward. Verify that at full forward the light is solid green.
            spin motor reverse<span style="color: rgb(231, 76, 60);"> the same speed as the joystick</span>
# Repeat the test for pulling back on the joystick.  This time the controller should flash red until fully pulled back, then turn solid red.
        }
# Verify that the motor moving is the correct one (the one next to Motor 5 from the previous lessons)
        else {
# Notice that when you pass the 1/4 mark, the motor jumps from stopped/off to a fairly substantial speed.  It doesn't scale from 0 smoothly.  For Part 2 we will address this.
            stop motor
 
        }
 
    }
= Part 2: Smoother Speed Control =
    else {
 
        stop motor
In this part, we will improve the control of the motor to smooth out control so that we get a full range of speed control.  The goal is to smooth out our speed response, particularly when the stick is moved just outside the deadzone.
    }
 
To accomplish this we will primarily use math knowledge!  You may find the use of graphs to be helpful - we have included some below!
 
== Part 2: Detailed Goals ==
* Use the same motor and joystick from Part 1 and use code from Part 1 as a starting point
* Right now we have a response that looks like this: [[File:Programming-simple-linear-deadband.png|320px|center|border]]
* The goal is to have a response that looks like this: [[File:Programming-smooth-linear-deadband.png|320px|center|border]]
 
== Part 2: Guided Help ==
Changing the response of the motor to the joystick's value is most easily done using math-based manipulations.
 
# Before you work on new code, take a moment and examine the problem statement in the Part 2 Detailed Goals above and the associated graphs. 
#* '''A large part of programming isn't actually writing code - it is figuring out what the problem to solve is and a good way to solve it!'''
#* On scrap paper or in a graphing tool of your choosing, write math equations (y = ?some function of x?) that represent the original and desired graphs above.  This can give you an excellent starting point for doing this in code.
#* You may find it easier to write separate equations for the different regions of x (x < -0.25, x in deadband from -0.25 to 0.25, and x > 0.25)
# Now that you have a mathematical relationship of the desired response, you can translate them to code:
#* The different regions can be handled using if statements
#* Math operations on variables can be done in a fairly straightforward manner.  Let's assume you had some variable y and some variable x, both of type double.  You can do math based on x and store the result in y.  "y" can then be used later in the program in if statements, more math operations, or passed to functions like motor controller's Set().  Here are some examples of math operations:
#** '''Addition:''' y = x + 4;
#** '''Subtraction:''' y = x - 1.5;
#** '''Multiplication:''' y = x * x; (y = "x squared")
#** '''Division:''' y = x / 2.0;
#** You can also do an operation on the same variable you are storing the result to.  These two things are exactly the same:
#*** y = y + 10;
#*** y += 10;
#*** Both of these add 10 to the value of y and store the result as the new value of y
#* '''Use the above examples and the equations you figured out at the start to change your code from Part 1 to achieve the desired functionality'''
 
== Part 2: Testing ==
 
* Repeat [[#Part 1: Testing]], but this time the response of the motor should be slower and smoother as you exit the deadzone on the controller.
 
= Solution =
 
'''This is one possible solution!'''  There are many ways to accomplish the goal(s) - this is just one way!
 
''robot.h''
<pre class="mw-collapsible mw-collapsed">
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
 
#pragma once
 
#include <frc/TimedRobot.h>
#include <ctre/Phoenix.h>
#include <frc/Joystick.h>
#include <frc/DigitalInput.h>
#include <frc/Jaguar.h>
#include <frc/Counter.h>
 
using namespace ctre::phoenix::motorcontrol::can;
using namespace frc;
 
class Robot : public frc::TimedRobot {
  // parts of a robot "appendage"
  TalonSRX motor{5};
  Joystick controller{0};
  DigitalInput limitSwitchFwd{1};
  DigitalInput limitSwitchRev{0};
 
  // parts of our robot's drive system
  TalonSRX driveMotor{3};
  Joystick driveJoystick{1};
 
 
public:
  void RobotInit() override;
  void RobotPeriodic() override;
 
  void AutonomousInit() override;
  void AutonomousPeriodic() override;
 
  void TeleopInit() override;
  void TeleopPeriodic() override;
 
  void DisabledInit() override;
  void DisabledPeriodic() override;
 
  void TestInit() override;
  void TestPeriodic() override;
};
</pre>
 
 
''robot.cpp''
<pre class="mw-collapsible mw-collapsed">
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
 
#include "Robot.h"
 
void Robot::RobotInit() {}
void Robot::RobotPeriodic() {}
 
void Robot::AutonomousInit() {}
void Robot::AutonomousPeriodic() {}
 
void Robot::TeleopInit() {}
void Robot::TeleopPeriodic() {
  // "appendage" control
  bool buttonOnePressed = false;
  buttonOnePressed = controller.GetRawButton(1);
  double leftYAxis = 0;
  // controller Y axis is inverted - forward is negative, reverse is positive
  // so flip it
  leftYAxis = -1 * controller.GetRawAxis(1);


NOTE: If you recall from the previous lessons, the joystick ranges from -1 to 1 and the motor also goes from -1 to 1. Also notice the changes to handle the fact that the joystick outputs negative values when the stick is moved forward.
  bool fwdLimitTripped = false;
  bool revLimitTripped = false;


&nbsp;
  fwdLimitTripped = limitSwitchFwd.Get();
  revLimitTripped = limitSwitchRev.Get();


After you have modified Robot.h and Robot.cpp, build the project. Once project builds without errors, let instructor know you are ready to try to delpoy and test. Do not deploy until told to.
  if (buttonOnePressed) {
    if (leftYAxis > 0.33 && fwdLimitTripped != true) {
      motor.Set(ControlMode::PercentOutput, 0.25);
    } else if (leftYAxis < -0.33 && !revLimitTripped) {
      motor.Set(ControlMode::PercentOutput, -0.25);
    } else {
      motor.Set(ControlMode::PercentOutput, 0);
    }
  } else {
    motor.Set(ControlMode::PercentOutput, 0);
  }


&nbsp;


<u><span style="font-size: large;">Test</span></u>
  // "drive" control
  double driveYAxis = 0;
  driveYAxis = -1 * driveJoystick.GetY();


#pull trigger and move joystick forward - motor should turn faster the more forward the joystick is moved
  // Part 1 solution - remove through to "end of part 1 solution"
#with the trigger pressed, pull the joystick backward - motor should turn, in opposite direction, faster the more backward the joystick is moved
  // if you uncomment part 2
  if (driveYAxis > 0.25 || driveYAxis < -0.25) {
    driveMotor.Set(ControlMode::PercentOutput, driveYAxis);
  } else {
    driveMotor.Set(ControlMode::PercentOutput, 0);
  }
  // End of part 1 solution


Did you notice that the motor speed just jumps to a speed, and does not ramp up from a stop?
  // Part 2 solution.  Uncomment this and remove the above
  // Part 1 solution section. This is one possible way to do this, there are
  // many other ways!
  /*
  if (driveYAxis > 0.25) {
    // Change from (0.25, 1] to (0, 0.75]
    driveYAxis -= 0.25;
    // Now normalize (linearly scale to be full range between 0 and 1)
    driveYAxis = driveYAxis / 0.75;
    driveMotor.Set(ControlMode::PercentOutput, driveYAxis);
  } else if (driveYAxis < -0.25) {
    // Similar to above case, but in negative space so reverse signs as needed
    // Change from (-0.25, -1] to (0, -0.75]
    driveYAxis += 0.25;
    // Now normalize (linearly scale to be full range between 0 and -1)
    driveYAxis = driveYAxis / 0.75;
    driveMotor.Set(ControlMode::PercentOutput, driveYAxis);
  } else {
    driveMotor.Set(ControlMode::PercentOutput, 0);
  }
  */
}


&nbsp;
void Robot::DisabledInit() {}
void Robot::DisabledPeriodic() {}


<u><span style="font-size: large;">Part 2</span></u>
void Robot::TestInit() {
}
void Robot::TestPeriodic() {}


Update the code so the motor speed starts at 0 when the joystick is at the end of the dead band. That is, when the stick is at -0.25 the motor will move 0 and when the stick is at -1 the motor will move +1. You will need to use your math skills to map one range of numbers to another range.
#ifndef RUNNING_FRC_TESTS
</div> <div class="mw-parser-output">&nbsp;</div> <div class="mw-parser-output">&nbsp;</div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div> </div>
int main() {
  return frc::StartRobot<Robot>();
}
#endif
</pre>

Latest revision as of 14:14, 30 December 2021

Introduction

This lesson will be split in two parts. Complete and test out part 1 before moving to part 2!

Overall, this lesson will use most of what you have already learned to do similar, but still different, things. Our previous lessons have focused on control that mimics some simple appendages. This lesson will focus on control that might be more appropriate for a drive system.

We will be using a different motor and controller for this lesson and all new code you write will not directly interact with your existing code from previous lessons (as in, you won't be changing your existing code, just adding new/additional things to it).

Before starting this lesson, you should have completed the prior 3 lessons. Start with your code from the end of Lesson 3

Part 1: Control Speed of Motor

In this part, you will use the Y-axis of a new Joystick to directly control the speed of a new motor. We will use a dead-band as we did in prior lessons to prevent accidental driving of the motor from small movement of the joystick.

Part 1 Detailed Goals

  • Use the Y-axis of the new Joystick on the driver station to control a new motor
  • If the stick is moved forward, the motor should go forward. If moved down/toward you, it should drive the motor backward.
  • The motor to control is the one immediately to the right of the one you have been using until now.
    • This motor is also controlled by a TalonSRX motor controller
    • You will need to look at the labels on the test board to know what the motor controller's ID/address number is
  • Use a deadband of +/- 1/4 of the Y axis range (so, motor should not move unless the stick is moved more than 1/4 of the travel)
  • The speed of the motor should directly match the amount that the stick is moved

Part 1 Guided Help

This lesson will have significantly less help than prior ones. Most of what you need to do is using things you have already done! Here are some ordered hints to get you there:

  1. You will need to make a new Joystick object to represent the new Joystick. Look at the driver station to get the address/ID number!
  2. You will need to make a new motor controller (TalonSRX) object to control the new motor. The ID of the new motor's controller is labelled on it on the test board.
  3. In prior lessons you made a deadband of 1/3rd the motion of the controller. Remember that for this lesson, we are using 1/4 the range of motion of the joystick for the deadband!
  4. Once you have handled the deadband, the speed of the motor can be simply set to the value read from the Joystick Y axis when you are outside of the deadband.

Part 1 Test

As always, coordinate with your teammates to use the test board to test. Test the following:

  1. The new motor should not move if the joystick is left stationary.
  2. Move the joystick a small amount off center. The motor should not move!
  3. Move the joystick forward more than 1/4 off center, slowly, toward fully forward. You should see the motor move slowly once you pass 1/4, then speed up and be going full speed when all the way forward. The LED on the motor controller should be flashing green while moving until you are fully forward. Verify that at full forward the light is solid green.
  4. Repeat the test for pulling back on the joystick. This time the controller should flash red until fully pulled back, then turn solid red.
  5. Verify that the motor moving is the correct one (the one next to Motor 5 from the previous lessons)
  6. Notice that when you pass the 1/4 mark, the motor jumps from stopped/off to a fairly substantial speed. It doesn't scale from 0 smoothly. For Part 2 we will address this.


Part 2: Smoother Speed Control

In this part, we will improve the control of the motor to smooth out control so that we get a full range of speed control. The goal is to smooth out our speed response, particularly when the stick is moved just outside the deadzone.

To accomplish this we will primarily use math knowledge! You may find the use of graphs to be helpful - we have included some below!

Part 2: Detailed Goals

  • Use the same motor and joystick from Part 1 and use code from Part 1 as a starting point
  • Right now we have a response that looks like this:
    Programming-simple-linear-deadband.png
  • The goal is to have a response that looks like this:
    Programming-smooth-linear-deadband.png

Part 2: Guided Help

Changing the response of the motor to the joystick's value is most easily done using math-based manipulations.

  1. Before you work on new code, take a moment and examine the problem statement in the Part 2 Detailed Goals above and the associated graphs.
    • A large part of programming isn't actually writing code - it is figuring out what the problem to solve is and a good way to solve it!
    • On scrap paper or in a graphing tool of your choosing, write math equations (y = ?some function of x?) that represent the original and desired graphs above. This can give you an excellent starting point for doing this in code.
    • You may find it easier to write separate equations for the different regions of x (x < -0.25, x in deadband from -0.25 to 0.25, and x > 0.25)
  2. Now that you have a mathematical relationship of the desired response, you can translate them to code:
    • The different regions can be handled using if statements
    • Math operations on variables can be done in a fairly straightforward manner. Let's assume you had some variable y and some variable x, both of type double. You can do math based on x and store the result in y. "y" can then be used later in the program in if statements, more math operations, or passed to functions like motor controller's Set(). Here are some examples of math operations:
      • Addition: y = x + 4;
      • Subtraction: y = x - 1.5;
      • Multiplication: y = x * x; (y = "x squared")
      • Division: y = x / 2.0;
      • You can also do an operation on the same variable you are storing the result to. These two things are exactly the same:
        • y = y + 10;
        • y += 10;
        • Both of these add 10 to the value of y and store the result as the new value of y
    • Use the above examples and the equations you figured out at the start to change your code from Part 1 to achieve the desired functionality

Part 2: Testing

  • Repeat #Part 1: Testing, but this time the response of the motor should be slower and smoother as you exit the deadzone on the controller.

Solution

This is one possible solution! There are many ways to accomplish the goal(s) - this is just one way!

robot.h

// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

#pragma once

#include <frc/TimedRobot.h>
#include <ctre/Phoenix.h>
#include <frc/Joystick.h>
#include <frc/DigitalInput.h>
#include <frc/Jaguar.h>
#include <frc/Counter.h>

using namespace ctre::phoenix::motorcontrol::can;
using namespace frc;

class Robot : public frc::TimedRobot {
  // parts of a robot "appendage"
  TalonSRX motor{5};
  Joystick controller{0};
  DigitalInput limitSwitchFwd{1};
  DigitalInput limitSwitchRev{0};

  // parts of our robot's drive system
  TalonSRX driveMotor{3};
  Joystick driveJoystick{1};


 public:
  void RobotInit() override;
  void RobotPeriodic() override;

  void AutonomousInit() override;
  void AutonomousPeriodic() override;

  void TeleopInit() override;
  void TeleopPeriodic() override;

  void DisabledInit() override;
  void DisabledPeriodic() override;

  void TestInit() override;
  void TestPeriodic() override;
};


robot.cpp

// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

#include "Robot.h"

void Robot::RobotInit() {}
void Robot::RobotPeriodic() {}

void Robot::AutonomousInit() {}
void Robot::AutonomousPeriodic() {}

void Robot::TeleopInit() {}
void Robot::TeleopPeriodic() {
  // "appendage" control
  bool buttonOnePressed = false;
  buttonOnePressed = controller.GetRawButton(1);
  double leftYAxis = 0;
  // controller Y axis is inverted - forward is negative, reverse is positive
  // so flip it
  leftYAxis = -1 * controller.GetRawAxis(1);

  bool fwdLimitTripped = false;
  bool revLimitTripped = false;

  fwdLimitTripped = limitSwitchFwd.Get();
  revLimitTripped = limitSwitchRev.Get();

  if (buttonOnePressed) {
    if (leftYAxis > 0.33 && fwdLimitTripped != true) {
      motor.Set(ControlMode::PercentOutput, 0.25);
    } else if (leftYAxis < -0.33 && !revLimitTripped) {
      motor.Set(ControlMode::PercentOutput, -0.25);
    } else {
      motor.Set(ControlMode::PercentOutput, 0);
    }
  } else {
    motor.Set(ControlMode::PercentOutput, 0);
  }


  // "drive" control
  double driveYAxis = 0;
  driveYAxis = -1 * driveJoystick.GetY();

  // Part 1 solution - remove through to "end of part 1 solution"
  // if you uncomment part 2
  if (driveYAxis > 0.25 || driveYAxis < -0.25) {
    driveMotor.Set(ControlMode::PercentOutput, driveYAxis);
  } else {
    driveMotor.Set(ControlMode::PercentOutput, 0);
  }
  // End of part 1 solution

  // Part 2 solution.  Uncomment this and remove the above
  // Part 1 solution section. This is one possible way to do this, there are
  // many other ways!
  /*
  if (driveYAxis > 0.25) {
    // Change from (0.25, 1] to (0, 0.75]
    driveYAxis -= 0.25;
    // Now normalize (linearly scale to be full range between 0 and 1)
    driveYAxis = driveYAxis / 0.75;
    driveMotor.Set(ControlMode::PercentOutput, driveYAxis);
  } else if (driveYAxis < -0.25) {
    // Similar to above case, but in negative space so reverse signs as needed
    // Change from (-0.25, -1] to (0, -0.75]
    driveYAxis += 0.25;
    // Now normalize (linearly scale to be full range between 0 and -1)
    driveYAxis = driveYAxis / 0.75;
    driveMotor.Set(ControlMode::PercentOutput, driveYAxis);
  } else {
    driveMotor.Set(ControlMode::PercentOutput, 0);
  }
  */
}

void Robot::DisabledInit() {}
void Robot::DisabledPeriodic() {}

void Robot::TestInit() {
}
void Robot::TestPeriodic() {}

#ifndef RUNNING_FRC_TESTS
int main() {
  return frc::StartRobot<Robot>();
}
#endif