diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..1afc436 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/base:trixie", + "features": { + "ghcr.io/devcontainers-extra/features/mkdocs:2": {}, + "ghcr.io/devcontainers/features/python:1": {} + }, + "postCreateCommand": "pip install --user -r requirements.txt" + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/workflows/Deploy_site.yml b/.github/workflows/Deploy_site.yml new file mode 100644 index 0000000..b052860 --- /dev/null +++ b/.github/workflows/Deploy_site.yml @@ -0,0 +1,32 @@ +name: Deploy Site +permissions: + contents: write +on: + # pull_request: + # branches: + # - robolancer_updates + push: + branches: + - robolancer_updates +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: pip install -r requirements.txt + - run: mkdocs build + + deploy: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.x + - run: pip install mkdocs + - run: pip install -r requirements.txt + - run: mkdocs gh-deploy --force \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c3e0def..3db0415 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,8 @@ name: Build MkDocs on: - pull_request: + push: branches: - - main + - robolancer_updates jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d244c53..1abbbf6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: Publish docs via GitHub Pages on: push: branches: - - main + - robolancer_updates jobs: deploy: runs-on: ubuntu-latest diff --git a/docs/assets/images/Electronics/Battery.jpg b/docs/assets/images/Electronics/Battery.jpg new file mode 100644 index 0000000..4e3d98d Binary files /dev/null and b/docs/assets/images/Electronics/Battery.jpg differ diff --git a/docs/assets/images/Electronics/Breaker.jpg b/docs/assets/images/Electronics/Breaker.jpg new file mode 100644 index 0000000..bb07ace Binary files /dev/null and b/docs/assets/images/Electronics/Breaker.jpg differ diff --git a/docs/assets/images/Electronics/CANWire.png b/docs/assets/images/Electronics/CANWire.png new file mode 100644 index 0000000..061ae9b Binary files /dev/null and b/docs/assets/images/Electronics/CANWire.png differ diff --git a/docs/assets/images/Electronics/CANcoder.jpg b/docs/assets/images/Electronics/CANcoder.jpg new file mode 100644 index 0000000..ad8f6d6 Binary files /dev/null and b/docs/assets/images/Electronics/CANcoder.jpg differ diff --git a/docs/assets/images/Electronics/CANivore.webp b/docs/assets/images/Electronics/CANivore.webp new file mode 100644 index 0000000..65c2c54 Binary files /dev/null and b/docs/assets/images/Electronics/CANivore.webp differ diff --git a/docs/assets/images/Electronics/Falcon.jpg b/docs/assets/images/Electronics/Falcon.jpg new file mode 100644 index 0000000..f9bb70f Binary files /dev/null and b/docs/assets/images/Electronics/Falcon.jpg differ diff --git a/docs/assets/images/Electronics/Kraken.png b/docs/assets/images/Electronics/Kraken.png new file mode 100644 index 0000000..b1ac958 Binary files /dev/null and b/docs/assets/images/Electronics/Kraken.png differ diff --git a/docs/assets/images/Electronics/LimeLight.png b/docs/assets/images/Electronics/LimeLight.png new file mode 100644 index 0000000..3345299 Binary files /dev/null and b/docs/assets/images/Electronics/LimeLight.png differ diff --git a/docs/assets/images/Electronics/LimitSwitch.jpg b/docs/assets/images/Electronics/LimitSwitch.jpg new file mode 100644 index 0000000..1931fdb Binary files /dev/null and b/docs/assets/images/Electronics/LimitSwitch.jpg differ diff --git a/docs/assets/images/Electronics/PDH.webp b/docs/assets/images/Electronics/PDH.webp new file mode 100644 index 0000000..4511e87 Binary files /dev/null and b/docs/assets/images/Electronics/PDH.webp differ diff --git a/docs/assets/images/Electronics/Pigeon2.webp b/docs/assets/images/Electronics/Pigeon2.webp new file mode 100644 index 0000000..8291995 Binary files /dev/null and b/docs/assets/images/Electronics/Pigeon2.webp differ diff --git a/docs/assets/images/Electronics/Pose.PNG b/docs/assets/images/Electronics/Pose.PNG new file mode 100644 index 0000000..8399410 Binary files /dev/null and b/docs/assets/images/Electronics/Pose.PNG differ diff --git a/docs/assets/images/Electronics/RSL.jpg b/docs/assets/images/Electronics/RSL.jpg new file mode 100644 index 0000000..4ebc5e1 Binary files /dev/null and b/docs/assets/images/Electronics/RSL.jpg differ diff --git a/docs/assets/images/Electronics/Radio.png b/docs/assets/images/Electronics/Radio.png new file mode 100644 index 0000000..07f69e8 Binary files /dev/null and b/docs/assets/images/Electronics/Radio.png differ diff --git a/docs/assets/images/Electronics/RevElectricalSystemDiagram.png b/docs/assets/images/Electronics/RevElectricalSystemDiagram.png new file mode 100644 index 0000000..068554b Binary files /dev/null and b/docs/assets/images/Electronics/RevElectricalSystemDiagram.png differ diff --git a/docs/assets/images/Electronics/RevThroughBore.webp b/docs/assets/images/Electronics/RevThroughBore.webp new file mode 100644 index 0000000..863330a Binary files /dev/null and b/docs/assets/images/Electronics/RevThroughBore.webp differ diff --git a/docs/assets/images/Electronics/RoboRIO.jpg b/docs/assets/images/Electronics/RoboRIO.jpg new file mode 100644 index 0000000..e03fabe Binary files /dev/null and b/docs/assets/images/Electronics/RoboRIO.jpg differ diff --git a/docs/assets/images/Electronics/Solenoid.jpg b/docs/assets/images/Electronics/Solenoid.jpg new file mode 100644 index 0000000..cdd9104 Binary files /dev/null and b/docs/assets/images/Electronics/Solenoid.jpg differ diff --git a/docs/assets/images/Electronics/SparkMax.jpg b/docs/assets/images/Electronics/SparkMax.jpg new file mode 100644 index 0000000..0636991 Binary files /dev/null and b/docs/assets/images/Electronics/SparkMax.jpg differ diff --git a/docs/assets/images/Electronics/VortexFlex.png b/docs/assets/images/Electronics/VortexFlex.png new file mode 100644 index 0000000..fa2eee8 Binary files /dev/null and b/docs/assets/images/Electronics/VortexFlex.png differ diff --git a/docs/assets/images/Electronics/servo.webp b/docs/assets/images/Electronics/servo.webp new file mode 100644 index 0000000..f71de31 Binary files /dev/null and b/docs/assets/images/Electronics/servo.webp differ diff --git a/docs/assets/images/Electronics/x44.webp b/docs/assets/images/Electronics/x44.webp new file mode 100644 index 0000000..39ce5d1 Binary files /dev/null and b/docs/assets/images/Electronics/x44.webp differ diff --git a/docs/assets/images/driving_robot/Quick_fix_import.png b/docs/assets/images/driving_robot/Quick_fix_import.png new file mode 100644 index 0000000..c0296d6 Binary files /dev/null and b/docs/assets/images/driving_robot/Quick_fix_import.png differ diff --git a/docs/assets/images/driving_robot/fix_error_1.PNG b/docs/assets/images/driving_robot/fix_error_1.PNG new file mode 100644 index 0000000..045a7f9 Binary files /dev/null and b/docs/assets/images/driving_robot/fix_error_1.PNG differ diff --git a/docs/assets/images/driving_robot/quick_fix_click.PNG b/docs/assets/images/driving_robot/quick_fix_click.PNG new file mode 100644 index 0000000..9fdc1b5 Binary files /dev/null and b/docs/assets/images/driving_robot/quick_fix_click.PNG differ diff --git a/docs/assets/images/new_project/Create.png b/docs/assets/images/new_project/Create.png new file mode 100644 index 0000000..afce274 Binary files /dev/null and b/docs/assets/images/new_project/Create.png differ diff --git a/docs/assets/images/vscode_tips/GitDemoVideo.mp4 b/docs/assets/images/vscode_tips/GitDemoVideo.mp4 new file mode 100644 index 0000000..5aca904 Binary files /dev/null and b/docs/assets/images/vscode_tips/GitDemoVideo.mp4 differ diff --git a/docs/assets/images/vscode_tips/git_graph_vscode.png b/docs/assets/images/vscode_tips/git_graph_vscode.png new file mode 100644 index 0000000..f86c5a7 Binary files /dev/null and b/docs/assets/images/vscode_tips/git_graph_vscode.png differ diff --git a/docs/assets/images/vscode_tips/git_vscode.png b/docs/assets/images/vscode_tips/git_vscode.png new file mode 100644 index 0000000..4d52f6f Binary files /dev/null and b/docs/assets/images/vscode_tips/git_vscode.png differ diff --git a/docs/assets/images/vscode_tips/github_GUI_Merge.png b/docs/assets/images/vscode_tips/github_GUI_Merge.png new file mode 100644 index 0000000..d01668b Binary files /dev/null and b/docs/assets/images/vscode_tips/github_GUI_Merge.png differ diff --git a/docs/assets/images/vscode_tips/github_branch_vscode.png b/docs/assets/images/vscode_tips/github_branch_vscode.png new file mode 100644 index 0000000..50490d9 Binary files /dev/null and b/docs/assets/images/vscode_tips/github_branch_vscode.png differ diff --git a/docs/assets/images/vscode_tips/github_pull_requests.png b/docs/assets/images/vscode_tips/github_pull_requests.png new file mode 100644 index 0000000..d980ad5 Binary files /dev/null and b/docs/assets/images/vscode_tips/github_pull_requests.png differ diff --git a/docs/basics/ElectronicsCrashCourse.md b/docs/basics/ElectronicsCrashCourse.md new file mode 100644 index 0000000..c23d797 --- /dev/null +++ b/docs/basics/ElectronicsCrashCourse.md @@ -0,0 +1,317 @@ + +# Electronics Crash Course + +## There are a lot of electrical components on an FRC robot, many of which we will need to interface with in software + +### Electrical system diagram + +Rev electrical system diagram + +This diagram shows many of the electrical components found on a typical FRC robot. +You don't need to memorize this layout, but its handy to have this as a reference. + +### Battery + +Battery + +All of the power our robot uses comes from a single 12-volt car battery. +These can be found on our battery cart in the corner of the lab. +Not all batteries are created equal: Some brands and specific batteries can hold more juice for longer than others. +One way we can see the performance of a battery is the resting voltage (or voltage when no motors or similarly power hungry devices are running) of the battery after it has charged. +This can be checked with the battery beak or on the driver station. +A good battery will have a resting voltage of over 13 volts, and any battery we use will be above 12. +As the battery is used the voltage will go down. +We tend to swap our batteries after they get to around 12.5 volts, although if we are testing something sensitive to voltage (like auto) we might swap them sooner. +After a match a good battery is usually still above 12 volts. +To track batteries so we know which batteries are "good" and which are "bad" we give them names like "Garbanzo" or "W.I.S. (Women In STEM)". + +When the robot is on, the voltage will drop as power is used by processors and lights. +When the robot is enabled, the voltage will drop further as power is actively used by mechanisms. +This is called voltage sag. +This means that we can't pull full power from every motor on the robot at once and have to be careful to design and program around our power budget. +When the voltage of the robot drops too low we can brownout. +This is usually first visible when the LEDs on the robot go out. +When the voltage is low enough, motors will begin to be turned off automatically, which is visible as a stuttering or jerking. +If the voltage is even lower, the robot can restart. +Needless to say, we want to avoid any brownout conditions in a match or when testing, but we often end up seeing them when pushing the robot in testing. + +$V = IR$ is the equation which governs voltage sag, where $V$ is amount the voltage will sag from resting, $I$ is the current draw of the robot, and $R$ is the internal resistance of the battery. + +### Main Breaker + +Main Breaker + +The main breaker is the power switch of the robot. +The red button will turn the robot off, and a small black lever on the side of the breaker will turn it on. +The main breaker is directly connected to the battery and limits the current draw of the robot, or the amount of power it can use at once. +This limit is theoretically 120a, but in practice we can draw much more than that for small but significant periods of time (several seconds). +Whenever you need to be hands on with the robot, make sure the breaker is off. +If you can't connect to the robot, make sure it's on. +If the breaker is on, make sure to wear safety glasses. + +The breaker should be mounted in a visible and accessible location on all robots, although it tends to blend in with its black casing. +We tend to have a 3d-printed guard over the off switch to prevent accidental presses by other robots mid-match. + +### Power Distribution Hub (PDH) + +PDH + +The PDH takes the power from the battery and distributes it to the motors, sensors, and other components on the robot (Almost like its a hub for distributing power!). +We don't have to do anything with the PDH in code, but if a motor or sensor does not have power it could have a bad connection with the PDH. +The PDH will also have a smaller breaker for each motor on it, which limits the power draw from each individual motor to 40 amps. +In practice, we can draw more for a short period of time. +If you are interested in more details about how and why that works, look at the electrical coursework for the team. + +There is a similar product manufactured by CTRE called the Power Distribution Panel, or PDP. +This is essentially an older version of the PDH with fewer motor slots, and is largely legacy hardware. +You might see it in older documentation and robots, however. + +The PDH is also often at one end of our CAN network. What's CAN? Glad you asked . . . + +### The CAN Network + +CAN Wire + +CAN is a type of communication along wires that allow our sensors and motors to communicate along our robot. +In FRC, CAN is transmitted over yellow and green pairs of cables. +Each device on the CAN network has a unique ID that it uses to talk to other devices on the network. +All of our motors and many of our sensors will communicate over CAN. +CAN is also one of the easiest places for electrical problems to become apparent in software because of the thin wires and large reach of the system. +When you start up the robot you may see red error messages in the drive station console reporting CAN errors. +If you see that, something might be broken on the CAN network. + +We have to set the IDs of the devices on the CAN network to make sure they are all uniquely and correctly identified. +It would be very awkward to try to drive the robot and accidentally run the elevator instead of the drivetrain! +To set the IDs of devices on the CAN network we use the [Tuner X](https://pro.docs.ctr-electronics.com/en/stable/docs/tuner/index.html) app built by CTRE. +This app also has features to test and configure devices on the CAN network. + +### CANivore + +CANivore + +The CANivore is a device that connects to the [Rio](#roborio-2) (in the next section) over usb. +It allows us to have a second CAN network with increased bandwidth. +This is useful because CAN has a limited amount of information that can travel over it each second, and we can easily reach that limit with the number of motors and sensors we have. +The CANivore gets around some hardware limitations of the Rio to have extra bandwidth on its network compared to the default network. +This also enables some extra latency compensation features on CTRE devices. +In 2024 we exclusively used the CANivore network for motors and sensors. + +### RoboRIO 2 + +RoboRIO + +The RoboRIO (rio) is the brain of the robot. +It is built around a computer running Linux with a large number of Input/Output (IO) ports. +These ports include: + +- Digital IO (DIO) can accept inputs and outputs that are either a 1 or 0, on or off. + When used as an input the signal can rapidly be turned on and off to send numerical data. + This is done using Pulse Width Modulation (PWM), essentially measuring how long a signal is on compared to how long it is off in a given time window to get a numerical value. + For more information on PWM optionally read [this article](https://learn.sparkfun.com/tutorials/pulse-width-modulation/all) +- PWM pins provide additional pins to _output_ PWM signals. + Unlike the DIO ports the PWM ports cannot take inputs. + Many motor controllers support using a PWM signal for control instead of CAN, although it has significantly limited features. +- Analog inputs provide adititional sensor ports, although most sensors use DIO or CAN. +- The large set of pins in the middle of the Rio is the MXP port. + MXP (and the SPI port in the top-right corner) is used for communication over serial interfaces such as I²C protocal. + Unfortunately, there is an issue with I²C that can cause the code to lock up when it is used, so we avoid using the serial ports. + We can get around this issue by using a coprocessor (computer other than the rio, like a raspberry pi) to convert the signal to a different protocol. + Generally we avoid using I²C devices. +- A CAN network originates at the RIO. +- Several USB ports are available on the Rio. + Common uses include external USB sticks to store logs or the [CANivore](#canivore) (see later in this page). + The usb ports can also be used to connect the rio to a computer to deploy code and run the robot, although we usually prefer to use ethernet if we need to run tethered. +- An ethernet port which connects to the radio (in the next section) to communicate to the driver station. + +The Rio also has an SD card as its memory. +When we set up a Rio for the season we need to image it using a tool that comes with the driver station. +The WPILib docs have [instructions](https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-3/roborio2-imaging.html) on how to image the SD card. + +### Note on 2026-7+ Control System +As per [this](https://community.firstinspires.org/introducing-the-future-mobile-robot-controller) blog post, FRC will use a different robot control system called SystemCore beginning with the 2027 season. +This moves away from the RoboRIO and NI and instead will use a controller based on the Raspberry Pi CM5. +More Information to come on Systemcore in 2026. + +### Vivid Hosting Radio + +Radio + +The VH-109 radio is essentially a Wi-Fi router that lives on the robot and connects it to the driver station. +At tournaments we have to take the radio to get reprogrammed for that competition. +This makes it able to connect to the access point (another radio on the field) so that our robot gets enabled and disabled at the right times during matches, however it prevents us from connecting to the robot with a laptop wirelessly $`^1`$. +The radio has four ethernet ports and a pair of terminals for power wires. +One ethernet port connects to the rio and is labeled RIO. +One is usually reserved for tethering to a laptop and is labeled DS. +We can connect certain advanced sensors, like vision systems, to ethernet. +Sometimes we add a network switch to the ethernet network, which is a device that allows us to have more ethernet ports than the radio provides. +Network switches and other ethernet devices are plugged into the AUX1 and AUX2 ports on the radio. + +After each competition we have to reimage the radio to allow it to connect to a laptop wirelessly again. +Refer to the [vivid hosting radio documentation](https://frc-radio.vivid-hosting.net/) for more information. + +We can also connect to the robot's VH-109 by connecting to a second network (which comes from a second radio that's connected to the robot). +This radio could be the VH-113 access point, which is a larger radio that is for the field instead of going on a robot, or a VH-109 configured to act like a VH-113. +This is the setup we'll usually be using at the Loom. + +The radio can either be powered using Power-Over-Ethernet (PoE) or the power terminals. +Be careful with checking for good, consistent radio power if you are having connection issues. + +An older radio known as the OM5P was in use until champs 2024, and you may encounter some on old/offseason robots. +It was much worse to deal with (more fragile and finicky) and we are lucky to be done with it. +It is pictured below. + +Old Radio + +### Motor Controllers + +Spark Max + +Motors are the primary form of movement on the robot, but on their own they are just expensive paperweights. +Motor controllers take signals from our code, often over CAN, and turn them into the voltage sent to the motor. +These controllers plug into slots on the PDH, the CAN network (or much more rarely PWM ports), and the power lines of the motor. + +Many motor controllers have more features than just commanding a voltage to a motor. +For instance they might be able to run PID loops much faster than our code is able to, which drives a motor to a specific position or velocity. +Knowing what motor controller you are using and what features it has is very important when writing robot code. +Pictured above is the Spark Max built by REV Robotics, a common modern motor controller often used with the NEO and NEO 550 motors. +REV also produces a motor called the NEO Vortex which uses the Spark Flex, pictured below. + +NEO Vortex + Spark Flex + + +### The Talon FX + Kraken X60/44 + +Kraken X60 + +**These motors are used by 321 only, but this information is included for reference.** + +The Kraken X60 ("Kraken") motor is the primary motor 321 uses on the robot. +Unlike many other motors, Krakens come with a built in motor controller called the Talon FX. +The Kraken also has a built in encoder, or sensor that tracks the rotation and speed of the motor. +This is a relative encoder, so we tend to pair them with CTRE CANcoders (which are absolute). +More on encoders [below](#encoders). +Documentation for the software to control Krakens can be found [here](https://pro.docs.ctr-electronics.com/en/stable/). +There is also the Kraken X44, which is mostly the same as the X60 but a bit smaller and lighter. +It looks the same in code to us, since it also has a TalonFX controller. + +Kraken X44 + +We also use the Falcon 500 ("Falcon") motor in some places on the robot. +Slightly less powerful, slightly older, and likely out of stock for the forseeable future, Falcons are slowly being phased out of our motor stock. +Because Falcons also have an integrated TalonFX, they behave exactly the same in code as Krakens. +A Falcon is pictured below. + +Falcon + +### Solenoids and Pneumatics + +A solenoid + +Pneumatics are useful for simple, linear, and repeatable motions where motors might not be ideal. +In the past we have used them primarily for extending intakes. +If pneumatic pistons are like motors, solenoids are like motor controllers. +A solenoid takes a control signal and uses it to switch incoming air into one of two output tubes to cause it to either extend or retract. +We usually use double solenoids, which have both powered extension and retraction. +Single solenoids only supply air for extension, and rely on the piston having a spring or something similar to retract them. +Our mechanical team has moved somewhat away from pneumatics over weight and complexity concerns, but they may still appear on future robots. + +### Servos + +A servo + +Servos are similar to motors in that they can produce rotational motion, but are designed for very precise rotation. +Servos are somewhat more common in FTC than FRC, but are good for "single use" mechanisms or something that needs to go to a specific position. +In 2025, we used 2 servos to open the funnel hatch panel in order to climb. + +### Robot Status Light + +RSL + +The Robot Status Light (RSL) is an orange light we are required to have on our robot by competition rules. +When the robot is powered on it will glow solid orange. +When the robot is enabled ie being controlled by joysticks or autonomous code it will flash orange. +This is handled automatically by WPILib. +**When the robot is enabled wear safety glasses and do not go near the robot**. + +We have additional LEDs on the robot that indicate the state of the robot, but they are not standardized year to year and should not be relied upon for safety information. + +## Sensors + +### Encoders + +CANcoder +Rev Through Bore ENcoder + +Encoders are devices that measure rotation. +We use them to measure the angles of our swerve modules, the speed of our wheels, the position of our elevator, and much more. +Relative encoders measure relative change in position (e.g. the shaft has rotated 30 degrees since powering on), while absolute encoders measure the exact position of the shaft (e.g. the shaft is 30 degrees from some absolute zero position). +Modern motors have relative encoders built in. + +Absolute encoders are useful when they're measuring something that cannot rotate more than once, because they only return their position in a 0-360 degree range. +This is well suited to mechanisms like arms, that usually can't rotate past 360 degrees without causing problems. +However, motors are almost always geared down such that the motor rotates multiple times per rotation of the mechanism it is attached to, making the absolute data not so absolute. +While this gearing is required to get enough torque from the motor, this is something to keep in mind when prototyping or looking at designs for mechanisms. +If a mechanism has an absolute encoder, it should be rotating 1 or less rotations over the mechanism's range. +If it is infeasible to have an encoder that goes 1 or less rotations for a mechanism, you might want to take a look at the next section, [Limit Switches](#limit-switches) + +Some examples of absolute encoders are the CTRE CANcoder (upper picture). +The CANcoder sends absolute position and velocity data over the CAN network and uses a magnet embedded in the mechanism to keep track of position. +We use these to track the heading of our swerve modules, as well as mechanisms like arms. + +The Rev Through Bore encoder (lower picture) solves the mounting problem by connecting directly to hex shaft (the most common type of shaft in FRC). +However, the Through Bore does not communicate over CAN and requires wiring to the DIO ports. +We used one of these on the hood of our 2022 robot. + +Some teams have seen success getting around the absolute encoder only having one rotation of usefullness by using [Chinese remainder theorem](https://en.wikipedia.org/wiki/Chinese_remainder_theorem) alongside multiple encoders. + +### Limit Switches + +Limit Switch + +A limit switch is a simple sensor that tells us whether a switch is pressed or not. +They are one of the electrically simplest sensors we can use, and are quite useful as a way to reset the encoder on a mechanism ("Zero" the mechanism). +They can also be used to set up safety limits for mechanisms so they don't damage themselves or others. +They are quite fragile, however, so absolute encoders are better to be used where possible. +We used a limit switch on our 2023 robot that was pressed when the elevator was fully retracted. +When it was pressed, we knew that the elevator must be fully retracted so we could reset the encoder to 0 inches of extension. + +However, due to the fragility and additional wiring necessary for limit switches we have moved away from them. +Instead, we use "current zeroing". +This involves slowly powering a mechanism into a hardstop with a known position (like an elevator being all the way down). +When the current draw of the motor spikes, we know that the motor is "stalling", or using power but not moving. +This tells us that we are at the hardstop in the same way that a limit switch would. +Think of it as walking towards a wall blindfolded with your hands outstretched- when you get to a point where you're not getting anywhere and are exerting a lot of force in front of you , you'll know you're at the wall. + +### IMU + +Pigeon 2 Gyro + +An Inertial Measurement Unit (IMU or sometimes Gyro) is a sensor that measures its acceleration and rotation. +We primarily use them to measure the rotation of the robot (heading) so that the robot knows which way it is pointing on the field. +This lets us run field-relative swerve drive. +Pictured above is the Pigeon 2.0 IMU by CTRE, an IMU that connects over the CAN network. + +### Beambreaks/Light Sensors + +A beambreak + +A beambreak has two parts: an emitter, which sends out a beam of infrared light, and a receiver, which is directly across from the emitter and receives that light. +These are useful for when we need to detect if something like a game piece is present or not. +If we mount the two parts on opposite sides of a mechanism, the game piece will block the light from reaching the other side and thus tell us that it's there. + +We might use an IR reflective sensor if we don't want to or can't add the receiver part, but this works in the same way by detecting when something's blocked the beam of light coming from the emitter. + +### Cameras and Vision + +A Limelight Camera + +Cameras and vision systems are an important part of advanced FRC software. +Vision in FRC can be used to detect visual markers called AprilTags on the field, game pieces, and even other robots. +The pictured camera is a Limelight camera, a purchaseable vision solution that connects to the robot over ethernet and we used from 2021-2023. + +However, we've moved towards custom hardware, such as [Arducam cameras](https://www.arducam.com/product/arducam-100fps-global-shutter-usb-camera-board-1mp-720p-ov9281-uvc-webcam-module-with-low-distortion-m12-lens-without-microphones-for-computer-laptop-android-device-and-raspberry-pi/) and [Orange Pi processors](http://www.orangepi.org/). +See the [Vision](..\3_Specifics\3.5_Vision.md) article for more details. +The cameras plug into the USB ports on the Orange Pi. +The Orange Pi connects to the radio over Ethernet, +It's powered off the PDH with a buck/step down converter (which decreases voltage and increases current, since the Orange Pi only takes 5V and not 12). +If you're having issues, check the PhotonVision docs pages on [networking](https://docs.photonvision.org/en/latest/docs/quick-start/networking.html) and [wiring](https://docs.photonvision.org/en/latest/docs/quick-start/wiring.html). \ No newline at end of file diff --git a/docs/programming/2.6_AdvantageKit.md b/docs/programming/2.6_AdvantageKit.md new file mode 100644 index 0000000..9e122f9 --- /dev/null +++ b/docs/programming/2.6_AdvantageKit.md @@ -0,0 +1,61 @@ + + +# AdvantageKit + +## AdvantageKit is a logging framework developed by team 6328 + +### What is logging? + +Logging is recording some or all of the state (such as the current values of variables, inputs and outputs, and what Commands are running) of the robot so that we can play it back later. + +### Why log? + +Logging helps with debugging by letting us see exactly what was happening to the robot and what it was doing when it broke. +This is especially useful at competition, when we have limited time and testing ability to diagnose problems. +For instance, at Houston 2023 we had an issue in our second quals match where our grabber stopped responding to input. +Using the logs of that match, we saw that the sensor readings of the grabber had stopped responding, which suggested that the CAN bus to the grabber had broken. + +### Why AdvantageKit? + +AdvantageKit logs every input and output to and from the robot. +This means we can perfectly recreate the state of the robot in the log or with a simulator. + +Diagram of AKit data flow + +It also means that we can run the same inputs through modified code, and simulate how the robot would have responded. +AdvantageKit calls this replay. +One of the examples 6328 uses is when they used a log to diagnose an issue with the way they tracked vision targets, adjusted it, then used replay to confirm that the change would have produced the correct outputs with the same inputs. + +AdvantageKit is a mature and developed framework for this type of logging that should continue to be maintained for the foreseeable future. + +AdvantageKit is closely integrated with AdvantageScope, a log and sim viewer built by 6328. + +### Drawbacks + +Running this amount of logging has performance overhead on the rio, using valuable cpu time each loop. +Logging also requires a non-insignificant architecture change to our codebase by using an IO layer under each of our subsystems. +While this does require some additional effort to write subsystems, it also makes simulating subsystems easier so it is a worthwhile tradeoff. + +8033-specific usage of AdvantageKit features will be covered in more detail in the next couple of lessons. + +### Resources + +- [AdvantageKit docs](https://docs.advantagekit.org/) +- [AdvantageKit repository](https://github.com/Mechanical-Advantage/AdvantageKit) +- [AdvantageScope log viewer](https://github.com/Mechanical-Advantage/AdvantageScope) +- [6328 logging talk](https://youtu.be/mmNJjKJG8mw) + +### Examples + +- [6328 2023 code](https://github.com/Mechanical-Advantage/RobotCode2023) +- [3476 2023 code](https://github.com/FRC3476/FRC-2023) +- [8033 2025 code](https://github.com/HighlanderRobotics/Reefscape) + +### Exercises + +- Follow this [tutorial](2.8_KitbotAKitRefactor.md) to add AdvantageKit to your kitbot code. + +### Notes + +- See the [AdvantageKit Structure Reference](2.7_AKitStructureReference.md) article for more on the IO layer structure +- [Here](https://drive.google.com/drive/folders/1qNMZ7aYOGI31dQNAwt7rhFo97NR7mxtr) are some logs from our 2023-2024 season diff --git a/docs/programming/autonomous.md b/docs/programming/autonomous.md index bed8a88..b0520fa 100644 --- a/docs/programming/autonomous.md +++ b/docs/programming/autonomous.md @@ -4,7 +4,7 @@ ## Overview -In this section we will be going over +In this section we will be going over: 1. Creating an autonomous command group 2. Using RobotPreferences to quickly change our autonomous values @@ -20,26 +20,37 @@ In this section we will be going over - An autonomous command is a command that is ran during "autonomous mode" under the **autonomousInit** method in **Robot.java** - It could be a single **command** or a **command group** -- It's especially helpful to have if you don't have any cameras to drive the robot during a -"sandstorm" period (2019 game mechanic where the drivers couldn't see during the pre tele-op phase) -- For this tutorial we will create an autonomous **command group** that makes the robot drive forward 5 feet, wait 5 seconds, and then pitch the shooter up during autonomous +- It's especially helpful to have if you don't have any cameras to drive the robot during autonomous (rare, but does happen) +- For this tutorial we will create a simple autonomous **command ** that makes the robot drive forward slightly. ## Creating Commands For Autonomous - Since we can't control our robot during an autonomous command we will want to create commands that allow the robot to move independently of a driver -## Creating the DriveDistance Command +## Creating a basic Autonomous Command !!! summary "" - **1)** Create a new command called **DriveDistance** + **1)** Create a new command called **AutoCommand** using the `create new class/command` feature in Vscode. + !!! summary "" **2)** Before the constructor create a **double** called **distance** + ```Java + private Double distance; + ``` - We will use this to tell the command to finish when the robot drives the inputted distance + + **3)** Also create a **Timer** called `runtime`. + + ```Java + private Time runTime; + ``` + + - This will be used to control how long the robot will move for. !!! summary "" - **3)** In the **DriveDistance** constructor add a **double** parameter called **inches** + **3)** In the **AutoCommand** constructor add a **DriveSubsystem** parameter called **driveSubsystem** !!! summary "" **4)** Inside type: diff --git a/docs/programming/driving_robot.md b/docs/programming/driving_robot.md index 2158e0f..66f162a 100644 --- a/docs/programming/driving_robot.md +++ b/docs/programming/driving_robot.md @@ -23,38 +23,38 @@ Before we begin we must create the class file for the drivetrain subsystem. See In the Drivetrain class we will tell the subsystem what type of components it will be using. -- A Drivetrain needs motor controllers. In our case we will use 4 Talon SRs (a brand of controller for motors). - - You could use other motor controllers such as Victor SPs or Talon SRXs but we will be using Talon SRs - - If you are using other motor controllers, replace Talon with TalonSRX, Victor, or VictorSP in the code you write depending on the type you use. +- A Drivetrain needs motor controllers. In our case we will use Neo SparkMaxes (a brand of controller for motors made by Rev Robotics). + - You could use other motor controllers such as Victor SPs or Talon SRs but we will be using NEO SparkMaxes + - If you are using other motor controllers, replace SparkMax with Talon, TalonSRX, Victor, or VictorSP in the code you write depending on the type you use. - You can use 2 motors (left and right), but for this tutorial we will use 4. !!! Tip Be sure to read [Visual Studio Code Tips](../basics/vscode_tips.md){target=_blank} before getting started! It will make your life a lot easier. -### Creating the Talon Variables +### Creating the SparkMax Variables !!! summary "" - **1)** Create 4 global variables of data type **Talon** and name them: `leftFrontTalon`, `rightFrontTalon`, `leftBackTalon`, `rightBackTalon` + **1)** Create 4 global variables of data type **SparkMax** and name them: `leftLeader`, `rightLeader`, `leftFollower`, `rightFollower` - - To get started type the word Talon followed by the name i.e. `#!java Talon leftFrontTalon;` - - These will eventually hold the object values for Talons and their port numbers. + - To get started type the word SparkMax followed by the name i.e. `#!java private Final SparkMax leftLeader;` + - These will eventually hold the object values for SparkMaxes, their port numbers, and their motor type (brushed or brushless). !!! summary "" - **2)** Next assign their values to `#!java null` ([more info on `null`](../basics/java_basics.md#overview){target=_blank}). + **2)** These are declared without values right now. - We do this to make sure it is empty at this point. - When we assign these variables a value, we will be getting the motor controller's port numbers out of Constants - This means we cannot assign them at the global level -??? Example +
Example The code you typed should be this: ```java - Talon leftFrontTalon = null; - Talon leftBackTalon = null; - Talon rightFrontTalon = null; - Talon rightBackTalon = null; + private final SparkMax leftLeader; + private final SparkMax leftFollower; + private final SparkMax rightLeader; + private final SparkMax rightFollower; ``` Your full **Drivetrain.java** should look like this: @@ -62,7 +62,8 @@ In the Drivetrain class we will tell the subsystem what type of components it wi ```java package frc.robot.subsystems; - import edu.wpi.first.wpilibj.Talon; + import com.revrobotics.spark.SparkLowLevel.MotorType; + import com.revrobotics.spark.SparkMax; import edu.wpi.first.wpilibj.command.Subsystem; /** @@ -71,42 +72,37 @@ In the Drivetrain class we will tell the subsystem what type of components it wi public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. - - Talon leftFrontTalon = null; - Talon leftBackTalon = null; - Talon rightFrontTalon = null; - Talon rightBackTalon = null; - + private final SparkMax leftLeader; + private final SparkMax leftFollower; + private final SparkMax rightLeader; + private final SparkMax rightFollower; + @Override public void periodic() { // This method will be called once per scheduler run } } ``` +
-??? fail "If an error occurs (red squiggles)" - 1. Click the word Talon - ![](../assets/images/driving_robot/e1.png) - 2. 💡 Click the light bulb - ![](../assets/images/driving_robot/e2.png) - 3. Select "Import 'Talon' (edu.wpi.first.wpilibj)" - ![](../assets/images/driving_robot/e3.png) - 4. Your error should be gone! +
If an error occurs (red squiggles)" + 1. Mouse Over the word SparkMax: The following menu should appear. + ![](../assets/images/driving_robot/fix_error_1.png) + 2. 💡 Click "quick fix" + ![](../assets/images/driving_robot/Quick_fix_click.png) + 3. Select "Import 'SparkMax' (com.revrobotics.spark)" + ![](../assets/images/driving_robot/quick_fix_import.PNG) + 4. Your error should be gone! +
### Creating and filling the constructor !!! summary "" - **1)** Create the constructor for Drivetrain.java ([more info on constructors](../basics/java_basics.md#constructors){target=_blank}) - - - The constructor is where we will assign values to our talon variables. - -!!! summary "" - Now that we have created the Talons we must initialize them and tell them what port on the roboRIO they are on. + Now that we have created the SparkMaxes and the Drive Constants we must initialize them and tell them what port on the roboRIO they are on. - **2)** Initialize (set value of) `leftFrontTalon` to `#!java new Talon(0)`. - - - This initializes a new talon, `leftFrontTalon`, in a new piece of memory and states it is on port 0 of the roboRIO. + **1)** Initialize (set value of) `leftLeader` to `#!java new SparkMax(LEFT_LEADER_ID, MotorType.KBrushed)`. + - This initializes a new SparkMax, `leftLeader`, in a new piece of memory and states it is on the port defined by `LEFT_LEADER_ID`. - This should be done within the constructor `#!java Drivetrain()` - This calls the constructor `#!java Talon(int)` in the Talon class. - The constructor `#!java Talon(int)` takes a variable of type `#!java int`. In this case the `#!java int` (integer) refers to the port number on the roboRIO. @@ -114,14 +110,14 @@ In the Drivetrain class we will tell the subsystem what type of components it wi ??? Info "roboRIO port diagram" ![](../assets/images/driving_robot/roboRIO_port.png) -??? Example + Example The code you typed should be this: ```java public Drivetrain() { - // Talons - leftFrontTalon = new Talon(0); + // Motors + leftLeader = new SparkMax(DriveConstants.LEFT_LEADER_ID, MotorType.kBrushed); } ``` @@ -130,8 +126,8 @@ In the Drivetrain class we will tell the subsystem what type of components it wi ```java package frc.robot.subsystems; - import edu.wpi.first.wpilibj.Talon; - import edu.wpi.first.wpilibj.command.Subsystem; + import com.revrobotics.spark.SparkLowLevel.MotorType; + import com.revrobotics.spark.SparkMax; /** * Add your docs here. @@ -140,14 +136,17 @@ In the Drivetrain class we will tell the subsystem what type of components it wi // Put methods for controlling this subsystem // here. Call these from Commands. - Talon leftFrontTalon = null; - Talon leftBackTalon = null; - Talon rightFrontTalon = null; - Talon rightBackTalon = null; + private final SparkMax leftLeader; + private final SparkMax leftFollower; + private final SparkMax rightLeader; + private final SparkMax rightFollower; public Drivetrain() { // Talons - leftFrontTalon = new Talon(0); + leftLeader = new SparkMax(0, MotorType.kBrushed); + leftFollower = new SparkMax(1 , MotorType.kBrushed); + rightLeader = new SparkMax(2, MotorType.kBrushed); + rightFollower = new SparkMax(3, MotorType.kBrushed); } @Override @@ -155,6 +154,7 @@ In the Drivetrain class we will tell the subsystem what type of components it wi // This method will be called once per scheduler run } ``` + ### Using Constants @@ -162,56 +162,56 @@ In the Drivetrain class we will tell the subsystem what type of components it wi Since each subsystem has its own components with their own ports, it is easy to lose track of which ports are being used and for what. To counter this you can use a class called **Constants** to hold all these values in a single location. -!!! summary "" - **1)** To use Constants, instead of putting `0` for the port on the Talon type: - ```java - Constants.DRIVETRAIN_LEFT_FRONT_TALON - ``` - + - Names should follow the pattern SUBSYSTEM_NAME_OF_COMPONENT - The name is all caps since it is a **constant** ([more info on constants](../basics/java_basics.md#constants){target=_blank}). !!! summary "" - **2)** Click on the underlined text - ![](../assets/images/driving_robot/constants/step_1.png) - -!!! summary "" - **3)** Click on the 💡light bulb and select “create constant…” - ![](../assets/images/driving_robot/constants/step_2.png) - -!!! summary "" - **4)** Click on Constants.java tab that just popped up - ![](../assets/images/driving_robot/constants/step_3.png) - + **1)** + Before we initalize the SparkMax objects we are going to create constants to hold the CAN ID's of the motors. This will happen in constants.java + - Inside the constants class, create a new class called `public static DriveConstants`. + - Inside `DriveConstants` class, create for constants called `LEFT_LEADER_ID`, `LEFT_FOLLOWER_ID`, `RIGHT_LEADER_ID`, and `RIGHT_FOLLOWER_ID`. + - Back in your `DriveTrain` class in `drivetrain.java`, import the `DriveConstants` class as follows: `Import frc.robot.Constants.DriveConstants;`. + + !!! Tip + Make sure to declare constants with `public static final` so they cannot be changed at runtime. + + !!! Danger + ***If you set this to the wrong value, you could damage your robot when it tries to move!*** !!! summary "" - **5)** Change the `0` to the correct port for that motor controller on your robot/roboRIO - ![](../assets/images/driving_robot/constants/step_4.png) - - !!! Danger - ***If you set this to the wrong value, you could damage your robot when it tries to move!*** + **1)** To use Constants, instead of putting `0` for the port in the SparkMax type: + ```Java + DriveConstants.LEFT_LEADER_ID + ``` !!! summary "" - **6)** Repeat these steps for the remaining Talons. + **2)** Replace the remaining numbers with constants. !!! Tip Remember to save both **Drivetrain.java** and **Constants.java** -??? Example +
Example The code you type should be this: - ```java - leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); - ``` + ```Java + public static final class DriveConstants { + public static final int LEFT_LEADER_ID = 1; + public static final int LEFT_FOLLOWER_ID = 2; + public static final int RIGHT_LEADER_ID = 3; + public static final int RIGHT_FOLLOWER_ID = 4; + } + ``` Your full **Drivetrain.java** should look like this: ```java package frc.robot.subsystems; - import edu.wpi.first.wpilibj.Talon; + import com.revrobotics.spark.SparkLowLevel.MotorType; + import com.revrobotics.spark.SparkMax; import edu.wpi.first.wpilibj.command.Subsystem; - import frc.robot.Constants; + import frc.robot.Constants.DriveConstants; /** * Add your docs here. @@ -220,18 +220,17 @@ Since each subsystem has its own components with their own ports, it is easy to // Put methods for controlling this subsystem // here. Call these from Commands. - Talon leftFrontTalon = null; - Talon leftBackTalon = null; - Talon rightFrontTalon = null; - Talon rightBackTalon = null; + private final SparkMax leftLeader; + private final SparkMax leftFollower; + private final SparkMax rightLeader; + private final SparkMax rightFollower; public Drivetrain() { // Talons - leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); - leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); - rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); - rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); - } + leftLeader = new SparkMax(DriveConstants.LEFT_LEADER_ID, MotorType.kBrushed); + leftFollower = new SparkMax(DriveConstants.LEFT_FOLLOWER_ID, MotorType.kBrushed); + rightLeader = new SparkMax(DriveConstants.RIGHT_LEADER_ID, MotorType.kBrushed); + rightFollower = new SparkMax(DriveConstants.RIGHT_FOLLOWER_ID, MotorType.kBrushed); @Override public void periodic() { @@ -246,16 +245,34 @@ Since each subsystem has its own components with their own ports, it is easy to package frc.robot; public class Constants { - // Talons - public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; - public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; - public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; - public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; + public static final class DriveConstants { + public static final int LEFT_LEADER_ID = 1; + public static final int LEFT_FOLLOWER_ID = 2; + public static final int RIGHT_LEADER_ID = 3; + public static final int RIGHT_FOLLOWER_ID = 4; + } } ``` !!! Warning Remember to use the values for **YOUR** specific robot or you could risk damaging it! +
+ +### Confguring the SparkMaxes +Each SparkMax motor must be configured with a CANTimeout. (How long to wait for a response from the motor controller) This is done as follows: +```Java +// Set can timeout. Because this project only sets parameters once on construction, the timeout can be long without blocking robot operation. Code which sets or gets parameters during operation may need a shorter timeout. + leftLeader.setCANTimeout(250); + rightLeader.setCANTimeout(250); + leftFollower.setCANTimeout(250); + rightFollower.setCANTimeout(250); +``` +Create the configuration to apply to motors. Voltage compensation helps the robot perform more similarly on different battery voltages (at the cost of a little bit of top speed on a fully charged battery). The current limit helps prevent tripping breakers. +```Java + SparkMaxConfig config = new SparkMaxConfig(); + config.voltageCompensation(12); + config.smartCurrentLimit(DriveConstants.DRIVE_MOTOR_CURRENT_LIMIT); +``` ### Creating the arcade drive @@ -273,16 +290,21 @@ Since each subsystem has its own components with their own ports, it is easy to !!! summary "" - **1)** In the same place we created our talons (outside of the constructor) we will create a **DifferentialDrive** and **SpeedControllerGroups** for our left and right motor controllers. + **1)** Create the DifferentialDrive object. Outside of the constructor type: + ```java + private final DifferentialDrive drive; + ``` + This defines the drive object that we will use to drive the robot. + In the constructor type: + ```java - SpeedControllerGroup leftMotors = null; - SpeedControllerGroup rightMotors = null; - - DifferentialDrive differentialDrive = null; + + drive = new DifferentialDrive(leftMotors, rightMotors); ``` + This defines the drive object. - Since DifferentialDrive only takes 2 parameters we need to create speed controller groups to combine like motor controllers together. - In this case we will combine the left motors together and the right motors together. @@ -291,47 +313,42 @@ Since each subsystem has its own components with their own ports, it is easy to You should only group motors that are spinning the same direction physically when positive power is being applied otherwise you could damage your robot. !!! summary "" - **2)** Now we must initialize the **SpeedControllerGroups** and **DifferentialDrive** like we did our talons. ... - - In the constructor type: - - ```java - leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); - rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); - - differentialDrive = new DifferentialDrive(leftMotors, rightMotors); + **2)** In order to configure the motors to drive correctly, we need to configure one on each side as the leader and one as the follower. + In the constructor we are goint to set the followe motors and link them to the leader motors. To do this we will need to include a couple more classes from the REV Library: + ```Java + import com.revrobotics.spark.SparkBase.PersistMode; + import com.revrobotics.spark.SparkBase.ResetMode; ``` - -??? Example - - The code you type outside the constructor should be this: - - ```java - SpeedControllerGroup leftMotors = null; - SpeedControllerGroup rightMotors = null; - - DifferentialDrive differentialDrive = null; + Then in the constructor, create the follower/leader config: + - Set configuration to follow leader and then apply it to correspondingfollower. Resetting in case a new controller is swapped in and persisting in case of a controller reset due to breaker trip. + + ```Java + config.follow(leftLeader); + leftFollower.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + config.follow(rightLeader); + rightFollower.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); ``` - The code you type inside the constructor should be this: - - ```java - leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); - rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); - - differentialDrive = new DifferentialDrive(leftMotors, rightMotors); + Remove following, then apply config to right leader + ```Java + config.disableFollowerMode(); + rightLeader.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + ``` + Set conifg to inverted and then apply to left leader. Set Left side inverted so that postive values drive both sides forward + ```Java + config.inverted(true); + leftLeader.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); ``` - Your full **Drivetrain.java** should look like this: +
Example - ```java + ```Java package frc.robot.subsystems; - import edu.wpi.first.wpilibj.SpeedControllerGroup; - import edu.wpi.first.wpilibj.Talon; + import com.revrobotics.spark.SparkLowLevel.MotorType; + import com.revrobotics.spark.SparkMax; import edu.wpi.first.wpilibj.command.Subsystem; - import edu.wpi.first.wpilibj.drive.DifferentialDrive; - import frc.robot.Constants; + import frc.robot.Constants.DriveConstants; /** * Add your docs here. @@ -340,35 +357,47 @@ Since each subsystem has its own components with their own ports, it is easy to // Put methods for controlling this subsystem // here. Call these from Commands. - Talon leftFrontTalon = null; - Talon leftBackTalon = null; - Talon rightFrontTalon = null; - Talon rightBackTalon = null; - - SpeedControllerGroup leftMotors = null; - SpeedControllerGroup rightMotors = null; - - DifferentialDrive differentialDrive = null; + private final SparkMax leftLeader; + private final SparkMax leftFollower; + private final SparkMax rightLeader; + private final SparkMax rightFollower; public Drivetrain() { // Talons - leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); - leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); - rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); - rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); - - leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); - rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); - - differentialDrive = new DifferentialDrive(leftMotors, rightMotors); - } - + leftLeader = new SparkMax(DriveConstants.LEFT_LEADER_ID, MotorType.kBrushed); + leftFollower = new SparkMax(DriveConstants.LEFT_FOLLOWER_ID, MotorType.kBrushed); + rightLeader = new SparkMax(DriveConstants.RIGHT_LEADER_ID, MotorType.kBrushed); + rightFollower = new SparkMax(DriveConstants.RIGHT_FOLLOWER_ID, MotorType.kBrushed); + + SparkMaxConfig config = new SparkMaxConfig(); + config.voltageCompensation(12); + config.smartCurrentLimit(DriveConstants.DRIVE_MOTOR_CURRENT_LIMIT); + + leftLeader.setCANTimeout(250); + rightLeader.setCANTimeout(250); + leftFollower.setCANTimeout(250); + rightFollower.setCANTimeout(250); + + config.follow(leftLeader); + leftFollower.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + config.follow(rightLeader); + rightFollower.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + + // Remove following, then apply config to right leader + config.disableFollowerMode(); + rightLeader.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + // Set conifg to inverted and then apply to left leader. Set Left side inverted + // so that postive values drive both sides forward + config.inverted(true); + leftLeader.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); @Override public void periodic() { // This method will be called once per scheduler run } } +} ``` +
#### Creating the arcadeDrive method @@ -377,7 +406,7 @@ Now it’s time to make an arcadeDrive from our differentialDrive! !!! summary "" **1)** Let’s create a public void method called “arcadeDrive” with type “double” parameters moveSpeed and rotateSpeed. - Below the constructor type: + Below the `periodic` method create a new method called `arcadeDrive`. This method will be called from our Drive command to actually move the robot. ```java public void arcadeDrive(double moveSpeed, double rotateSpeed) { @@ -393,8 +422,8 @@ Now it’s time to make an arcadeDrive from our differentialDrive! Inside our method type: - ```java - differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); + ```Java + drive.arcadeDrive(moveSpeed, rotateSpeed); ``` DifferentialDrive's arcadeDrive method takes parameters moveValue and rotateValue. @@ -408,26 +437,25 @@ Now it’s time to make an arcadeDrive from our differentialDrive! You may want to do this for initial testing to make sure everything is going the right direction. -??? Example +
Drive Example The code you type should be this: - ```java + ```Java public void arcadeDrive(double moveSpeed, double rotateSpeed) { - differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); + drive.arcadeDrive(moveSpeed, rotateSpeed); } ``` Your full **Drivetrain.java** should look like this: - ```java - package frc.robot.subsystems; + ```Java + package frc.robot.subsystems; - import edu.wpi.first.wpilibj.SpeedControllerGroup; - import edu.wpi.first.wpilibj.Talon; + import com.revrobotics.spark.SparkLowLevel.MotorType; + import com.revrobotics.spark.SparkMax; import edu.wpi.first.wpilibj.command.Subsystem; - import edu.wpi.first.wpilibj.drive.DifferentialDrive; - import frc.robot.Constants; + import frc.robot.Constants.DriveConstants; /** * Add your docs here. @@ -436,111 +464,53 @@ Now it’s time to make an arcadeDrive from our differentialDrive! // Put methods for controlling this subsystem // here. Call these from Commands. - Talon leftFrontTalon = null; - Talon leftBackTalon = null; - Talon rightFrontTalon = null; - Talon rightBackTalon = null; - - SpeedControllerGroup leftMotors = null; - SpeedControllerGroup rightMotors = null; - - DifferentialDrive differentialDrive = null; + private final SparkMax leftLeader; + private final SparkMax leftFollower; + private final SparkMax rightLeader; + private final SparkMax rightFollower; public Drivetrain() { // Talons - leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); - leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); - rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); - rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); - - leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); - rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); - - differentialDrive = new DifferentialDrive(leftMotors, rightMotors); - } - - public void arcadeDrive(double moveSpeed, double rotateSpeed) { - differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); - } - + leftLeader = new SparkMax(DriveConstants.LEFT_LEADER_ID, MotorType.kBrushed); + leftFollower = new SparkMax(DriveConstants.LEFT_FOLLOWER_ID, MotorType.kBrushed); + rightLeader = new SparkMax(DriveConstants.RIGHT_LEADER_ID, MotorType.kBrushed); + rightFollower = new SparkMax(DriveConstants.RIGHT_FOLLOWER_ID, MotorType.kBrushed); + + SparkMaxConfig config = new SparkMaxConfig(); + config.voltageCompensation(12); + config.smartCurrentLimit(DriveConstants.DRIVE_MOTOR_CURRENT_LIMIT); + + leftLeader.setCANTimeout(250); + rightLeader.setCANTimeout(250); + leftFollower.setCANTimeout(250); + rightFollower.setCANTimeout(250); + + config.follow(leftLeader); + leftFollower.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + config.follow(rightLeader); + rightFollower.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + + // Remove following, then apply config to right leader + config.disableFollowerMode(); + rightLeader.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); + // Set conifg to inverted and then apply to left leader. Set Left side inverted + // so that postive values drive both sides forward + config.inverted(true); + leftLeader.configure(config, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); @Override public void periodic() { // This method will be called once per scheduler run } } - ``` - -*** - -## Making our robot controllable - -### Creating the Joystick - -In order to drive our robot, it needs to know what will be controlling it. To do so, we will create a new joystick in RobotContainer.java - -!!! summary "" - **1)** Open RobotContainer.java - - **2)** Type:  - ```java - public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); - ``` - - - - - Import any classes if necessary such as: `#!java import edu.wpi.first.wpilibj.Joystick;` - - A variable `driverController` of type Joystick pointing to a joystick on port `DRIVER_CONTROLLER` from **Constants** - - **3)** Click the 💡 light bulb to create a new **CONSTANT** and set the value to the port number the joystick uses on the laptop (this can be found in the Driverstation software). - - - -??? Example - - The code you type should be this: - - ```java - public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); - ``` - - Your full **RobotContainer.java** should look like this: - - ```java - package frc.robot; - - import edu.wpi.first.wpilibj.Joystick; - - /** - * This class is where the bulk of the robot should be declared. Since - * Command-based is a "declarative" paradigm, very little robot logic should - * actually be handled in the {@link Robot} periodic methods (other than the - * scheduler calls). Instead, the structure of the robot (including subsystems, - * commands, and button mappings) should be declared here. - */ - public class RobotContainer { - // The robot's subsystems and commands are defined here... - public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); + public void arcadeDrive(double moveSpeed, double rotateSpeed) { + drive.arcadeDrive(moveSpeed, rotateSpeed); } ``` +
- Your full **Constants.java** should look similar to this: - - ```java - package frc.robot; - - public class Constants { - // Talons - public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; - public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; - public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; - public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; - - // Joysticks - public static final int DRIVER_CONTROLLER = 0; - } - ``` +### Making our robot controllable -### Creating the DriveArcade Command +### Creating the Drivearcade Command - Remember that **methods** tell the robot what it can do but in order to make it do these things we must give it a **command**. See [Command Based Robot](../basics/wpilib.md#command-based-robot){target=_blank} - Now that we have created the method, we need to create a command to call and use that method. @@ -548,16 +518,51 @@ In order to drive our robot, it needs to know what will be controlling it. To do Before we begin we must create the class file for the DriveArcade command. See [Creating a New Command](new_project.md#creating-a-new-command){target=_blank} for info on how to do this and info on what each pre-created method does. +#### Define variables + +!!! summary "" + **1)** Create `xspeed` and `zrotation` variables. (to be passed to drive subsystem). These will be declared as `DoubleSuppliers`, which is a function that return a type. This is important for later. + **2)** Create an emtpy `driveSubsystem` instance of `DriveSubsystem` + + !!! Warning + `DoubleSupplier` and `DriveSubsystem` will have to be imported as follows: + ```Java + import frc.robot.subsystems.DriveSubsystem; + import java.util.function.DoubleSupplier; + ``` + + ```Java + private final DoubleSupplier xSpeed; + private final DoubleSupplier zRotation; + private final DriveSubsystem driveSubsystem; + ``` + #### In the constructor !!! summary "" - **1)** In the constructor `#!java DriveArcade()` type: + **1)** Inside the parenthesis of the the constructor `driveArcade()` add 3 variables: + ```Java + public DriveCommand( + DoubleSupplier xSpeed, DoubleSupplier zRotation, DriveSubsystem driveSubsystem) + ``` + These are values that will be passed into the command in `RobotContainer.java` + +!!! summary "" + **2)** Inside constructor `#!java DriveArcade()` type: ```java - addRequirements(RobotContainer.m_drivetrain); + this.xSpeed = xSpeed; + this.zRotation = zRotation; + this.driveSubsystem = driveSubsystem; + addRequirements(this.drivetrain); ``` - - This means, this command will end all other commands currently using drivetrain and will run instead when executed. + - The 3 lines starting with `this` set the global variables we defined at the top of our class file to the values being passed into the consturctor. +!!! Tip + `this` is how the class instance `object` refers to itself in code. + +!!! summary "" + - `addRequirements` means this command will end all other commands currently using drivetrain and will run instead when executed. - It also means, other commands that require drivetrain will stop this command and run instead when executed. !!! Warning @@ -566,30 +571,12 @@ Before we begin we must create the class file for the DriveArcade command. See [ #### In the execute method !!! summary "" - **1)** In the execute method we will create 2 variables of type double called moveSpeed and rotateSpeed. - - - We want these variables to be the value of the axis of the controller we are using to drive the robot. So we will set them equal to that by using the joystick getRawAxis method. - - Controllers return an axis value between 1 and -1 to indicate how far the joystick is pushed up or down. Our personal controller returns up as -1 so we want to invert it. - - In Java you can put a negative “ - “in front of a numeric value to invert it (value * -1) - - The joystick’s getRawAxis method will get the position value of the axis as you move it. The method takes parameter “axis number.” (This can be found in the Driverstation software and we will store it in Constants). - - In the execute() method type: - - ```java - double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS); - double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS); - ``` - - !!! Tip - Remember to use the light bulb for importing and creating constants if needed! - -!!! summary "" - **2)** Also in the execute method we will we want to call the **arcadeDrive** method we created in **Drivetrain** and give it the variables **moveSpeed** and **rotateSpeed** we created as parameters. + **1)** In the execute method we will we want to call the **arcadeDrive** method we created in **Drivetrain** and give it the variables **moveSpeed** `xspeed` and **rotateSpeed** `zrotation` we created as parameters. In the execute() method below rotateSpeed type: - ```java - RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed); + ```Java + driveSubsystem.driveArcade(xSpeed.getAsDouble(), zRotation.getAsDouble()); ``` #### In the isFinished method @@ -610,7 +597,7 @@ Since we will be using this command to control the robot we want it to run indef #### In the end method !!! summary "" - **1)** We will call the arcadeDrive method and give it 0 and 0 as the parameters. + **1)** We will call the arcadeDrive method and give it 0 and 0 as the parameters. this will stop the robot when the command completes. In the end() method type: @@ -622,7 +609,7 @@ Since we will be using this command to control the robot we want it to run indef #### Completed Example -??? Example +
Example Your full **Constants.java** should look similar to this: @@ -630,32 +617,45 @@ Since we will be using this command to control the robot we want it to run indef package frc.robot; public class Constants { - // Talons - public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; - public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; - public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; - public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; - - // Joysticks - public static final int DRIVER_CONTROLLER = 0; - public static final int DRIVER_CONTROLLER_MOVE_AXIS = 1; // Change for your controller - public static final int DRIVER_CONTROLLER_ROTATE_AXIS = 2; // Change for your controller + public static final class DriveConstants { + public static final int LEFT_LEADER_ID = 1; + public static final int LEFT_FOLLOWER_ID = 2; + public static final int RIGHT_LEADER_ID = 3; + public static final int RIGHT_FOLLOWER_ID = 4; + public static final int DRIVE_MOTOR_CURRENT_LIMIT = 60; + } + + public static final class OperatorConstants { + public static final int DRIVER_CONTROLLER_PORT = 0; + public static final int OPERATOR_CONTROLLER_PORT = 1; + } } ``` Your full **DriveArcade.java** should look like this: - ```java - package frc.robot.commands; - - import edu.wpi.first.wpilibj.command.Command; - import frc.robot.RobotContainer; - import frc.robot.Constants; - - public class DriveArcade extends Command { - public DriveArcade() { - // Use addRequirements() here to declare subsystem dependencies. - addRequirements(RobotContainer.m_drivetrain); + ```Java + import edu.wpi.first.wpilibj2.command.Command; + import frc.robot.subsystems.DriveSubsystem; + import java.util.function.DoubleSupplier; + + // Command to drive the robot + public class DriveCommand extends Command { + private final DoubleSupplier xSpeed; + private final DoubleSupplier zRotation; + private final DriveSubsystem driveSubsystem; + + // Constructor. Runs only once when the command is first created. + public DriveCommand( + DoubleSupplier xSpeed, DoubleSupplier zRotation, DriveSubsystem driveSubsystem) { + // Save parameters to local variables for use later + this.xSpeed = xSpeed; + this.zRotation = zRotation; + this.driveSubsystem = driveSubsystem; + + // Declare subsystems required by this command. This should include any + // subsystem this command sets and output of + addRequirements(this.driveSubsystem); } // Called just before this Command runs the first time @@ -666,16 +666,13 @@ Since we will be using this command to control the robot we want it to run indef // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS); - double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS); - - RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed); + driveSubsystem.driveArcade(xSpeed.getAsDouble(), zRotation.getAsDouble()); } // Called once the command ends or is interrupted. @Override protected void end(boolean interrupted) { - Robot.m_drivetrain.arcadeDrive(0, 0); + driveSubSystem.arcadeDrive(0, 0); } // Make this return true when this Command no longer needs to run execute() @@ -685,36 +682,65 @@ Since we will be using this command to control the robot we want it to run indef } } ``` +
-### Using setDefaultCommand +### Creating the Joystick + +In order to drive our robot, it needs to know what will be controlling it. To do so, we will use the joystick in `RobotContainer.java`, as `m_drivecontroller`. -- Commands passed to this method will run when the robot is enabled. -- They also run if no other commands using the subsystem are running. - - This is why we write **addRequirements(Robot.m_subsystemName)** in the commands we create, it ends currently running commands using that subsystem to allow a new command is run. - !!! summary "" - **1)** Back in **RobotContainer.java** in the constructor we will call the `setDefaultCommand` of `m_drivetrain` and pass it the `DriveArcade` command + **1)** Open Constants.java + Check and make sure the `kDriverControllerPort` constant is present. + **2)** Open RobotContainer.java + - in the imports section, change `ExampleCommand` to `DriveCommand`. + - inside the class, find the line ` private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem();` and change `ExampleSubsystem` to `DriveSubsystem` and `m_exampleSubsystem` to `drivetrain`. - In the **RobotContainer.java** constructor type: + - ```java - m_drivetrain.setDefaultCommand(new DriveArcade()); +### Using setDefaultCommand + +!!! summary "" + **1)** Back in **RobotContainer.java** We will need to remove everything inside the `configureBindings` method. + **2)** in the `configureBindings`we will call the `setDefaultCommand` of `drivetrain` and create a new `DriveArcade` command with parameters. + + !!! Tip + - Commands in this method will run when the robot is enabled. + - They also run if no other commands using the subsystem are running. + - This is why we write **addRequirements(Robot.subsystemName)** in the commands we create, it ends currently running commands using that subsystem to allow a new command is run. + - We will the default command for the drive subsystem to an instance of the `DriveArcade` with the values provided by the joystick axes on the driver controller. + - The Y axis of the controller is inverted so that pushing the stick away from you (a negative value) drives the robot forwards (a positive value). + - Similarly for the X axis where we need to flip the value so the joystick matches the WPILib convention of counter-clockwise positive + + ```Java + driveSubsystem.setDefaultCommand(new DriveArcade( + () -> -m_driverController.getLeftY() + () -> -m_driverController.getRightX(), + driveSubsystem)); ``` + !!! Tip + - Notice the `()->` notation above. This notation creates lamdas or anonymous methods. [More about Lambdas](https://www.w3schools.com/java/java_lambda.asp){target=blank} + - The lambas are required because we set the parameter types of `xpeed` and 'zrotation' in our `DriveCommand` to be `DoubleSuppliers`, which are methods that return doubles. (Which is what the lambdas above return.) + - These are declared as such so that they get and send the updated values from `m_driverController.getLeftY()` and `m_driverController.getRightX()` to the drive motors continuously. + !!! Tip Remember to use the light bulb for importing if needed! + !!! Tip + The `New` keyword creates a new instance of a class (object) -??? Example +
Full RobotContainer Example Your full **RobotContainer.java** should look like this: ```java package frc.robot; - import edu.wpi.first.wpilibj.Joystick; - import frc.robot.commands.*; - import frc.robot.subsystems.*; - import edu.wpi.first.wpilibj2.command.Command; + import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + import edu.wpi.first.wpilibj2.command.button.Trigger; + import frc.robot.Constants.OperatorConstants; + import frc.robot.commands.Autos; + import frc.robot.commands.DriveCommand; + import frc.robot.subsystems.DriveSubsystem; /** * This class is where the bulk of the robot should be declared. Since Command-based is a @@ -724,12 +750,11 @@ Since we will be using this command to control the robot we want it to run indef */ public class RobotContainer { // The robot's subsystems and commands are defined here... - public static final Drivetrain m_drivetrain = new Drivetrain(); - private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); - - private final ExampleCommand m_autoCommand = new ExampleCommand(m_exampleSubsystem); + public final DriveSubsystem drivetrain = new DriveSubsystem(); - public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); + private final DriveCommand m_autoCommand = new DriveCommand(drivetrain); + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); /** * The container for the robot. Contains subsystems, OI devices, and commands. @@ -738,8 +763,7 @@ Since we will be using this command to control the robot we want it to run indef // Configure the button bindings configureButtonBindings(); - // Set default commands on subsystems - m_drivetrain.setDefaultCommand(new DriveArcade()); + } /** @@ -749,6 +773,10 @@ Since we will be using this command to control the robot we want it to run indef * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. */ private void configureButtonBindings() { + driveSubsystem.setDefaultCommand(new DriveSubSystem( + () -> -driverController.getLeftY() + () -> -driverController.getRightX(), + driveSubsystem)); } @@ -763,3 +791,4 @@ Since we will be using this command to control the robot we want it to run indef } } ``` +
\ No newline at end of file diff --git a/docs/programming/new_project.md b/docs/programming/new_project.md index 0e7bc3a..67aa8cd 100644 --- a/docs/programming/new_project.md +++ b/docs/programming/new_project.md @@ -59,14 +59,16 @@ Newly created projects have many files within them. We only care about the conte - An example Command - **ExampleSubsystem.java** - An example SubSystem -- **Constants.java** (new in 2020, replaces RobotMap.java) +- **Constants.java** - Used to map physical ports (digital if using the CAN bus) of sensors or devices connected to the robot and assign them a variable name to be used in other parts of the code. - This provides flexibility for changing wiring, makes checking the wiring easier, and significantly reduces the number of magic numbers floating around. - Can also be used to store generic constant values as variables in the code + - This gives a unified place to store and modify values that might be used many places in the code, so they only have to be changes once. + - **Main.java** - Used for advanced programming - Will be ignored/left as is for this tutorial -- **RobotContainer.java** (new in 2020, replaces OI.java) +- **RobotContainer.java** - Used to declare our subsystem - Used to create a connection between commands and Operator Interfaces (OI) such as Joysticks or buttons - **Robot.java** diff --git a/docs/version_control/BasicGit.md b/docs/version_control/BasicGit.md new file mode 100644 index 0000000..b7697ff --- /dev/null +++ b/docs/version_control/BasicGit.md @@ -0,0 +1,112 @@ + +# Basic Git + +------------ + +Git is a version control system, or a way for us to collaborate on code and manage having multiple versions of code working in parallel + +While many modern cloud applications, like google docs, have some form of auto-save to the cloud, we don't necessarily want a single auto-updating version of our code. +As we're working, the changes we make can cause code to break very quickly in ways that might not be immediately obvious, which means it's good to be able to take a "snapshot" of the code at a point where it worked. +So, we use a tool called Git to manage different versions of our files and save them to Github, which lets us share the codebase between computers. +Instead of automatically syncing to Github, we save our code in small named chunks called commits. +These named versions makes it easy to revert changes that break previously working behaviour and see when and by who code was written. +Git also lets us have different, parallel versions, called branches, of code. +This means that while one person works on code for the autonomous, another could work on vision, for example, without overriding each other. + +## Resources + +Read one of the following: + +- [WPILib Git intro](https://docs.wpilib.org/en/stable/docs/software/basic-programming/git-getting-started.html) +- [Github Git intro](https://docs.github.com/en/get-started/using-git/about-git) +- [Github Git intro video (start at 0:43)](https://youtu.be/r8jQ9hVA2qs?t=43) + +Install the following: + +- [Windows Git install](https://gitforwindows.org/) +- [Linux/Mac Git install](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Github Desktop](https://desktop.github.com/download/) + +## Basic Git Commands + +To use Git, developers use specific commands to copy, create, change, and combine code. These commands can be executed directly from the command line or by using an application like GitHub Desktop. Here are some common commands for using Git: + +**git init**: initializes a brand new Git repository and begins tracking an existing directory. It adds a hidden subfolder within the existing directory that houses the internal data structure required for version control. + +**git clone** creates a local copy of a project that already exists remotely. The clone includes all the project's files, history, and branches. + +**git add** stages a change. Git tracks changes to a developer's codebase, but it's necessary to stage and take a snapshot of the changes to include them in the project's history. This command performs staging, the first part of that two-step process. Any changes that are staged will become a part of the next snapshot and a part of the project's history. Staging and committing separately gives developers complete control over the history of their project without changing how they code and work. + +**git commit** saves the snapshot to the project history and completes the change-tracking process. In short, a commit functions like taking a photo. Anything that's been staged with git add will become a part of the snapshot with git commit. + +**git status** shows the status of changes as untracked, modified, or staged. + +**git branch** shows the branches being worked on locally. + +**git merge** merges lines of development together. This command is typically used to combine changes made on two distinct branches. For example, a developer would merge when they want to combine changes from a feature branch into the main branch for deployment. + +**git pull** updates the local line of development with updates from its remote counterpart. Developers use this command if a teammate has made commits to a branch on a remote, and they would like to reflect those changes in their local environment. + +**git push** updates the remote repository with any commits made locally to a branch. + +For more information, see the [full reference guide to git commands](https://git-scm.com/docs). + +## Git Log + +>Git log is a tool to view your history in Git. By default Git Log shows all of the commits in the repository, ordered from newest to oldest. in VSCode, the graph panels shows the Git log/History in a visual way. +![Graph view in VS code](../assets/images/vscode_tips/git_graph_vscode.png) + +## Github Setup + +Github is a website that hosts Git repositories. The Robolancers store all of their codes on Github. + +1. Sign up for a Github Account here: [Sign up for Github](https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F&source=header-home) +2. Enable 2 factor Authentitaction (Required to add code): [2fa setup](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) + +## Examples + +![A simple demonstration of committing and pushing some changes in git](../assets/images/vscode_tips/GitExample.png) + +- Typing `git add .` then `git commit -m "commit name"` then `git push` is the bread and butter of using Git. + This sequence tells Git to collect all of your uncommitted changes, commit them, then push them to Github. + If you'd like to combine these into one command, you can add an alias to your .gitconfig file (ask a lead or mentor for help). +- `git checkout branch-name` switches between branches +- `git checkout -b new-branch` makes a new branch and switches to it. + Note that the first time you push from this branch you will need to enter some extra parameters, but the terminal should prompt you with the correct command when you enter `git push` +- `git status` displays the current branch and what changes you have uncommitted and staged +- `git pull` updates the code on your device with the latest code from Github. + Recommended to do this whenever someone else has been working on the same branch, otherwise you might make conflicting changes +- [A simple demo video of committing some changes](../assets/images/vscode_tips/GitDemoVideo.mp4) + +## Managing Git in Vscode + +### Git Commands in Vscode GUI + +![Git commands in vscode GUI](../assets/images/vscode_tips/git_vscode.png) + +### Git branches menu + +Accessed by clicking on the branch name at the bottom left +![Github branch menu in Vscode GUI](../assets/images/vscode_tips/github_branch_vscode.png) + +### Merge in another branch in vscode + +![Github Merge menu](../assets/images/vscode_tips/github_GUI_Merge.png) + +## Notes + +- We use GitHub's pull request (PR) feature to manage branches and merges. +Always make sure to merge to main using a PR. +PR's will be explained further in the [Git Workflow docs](1.3_GitWorkflow.md). +- Always commit code that at the very least compiles (you can check this by running the "Build robot code" option in WPILib's command bar) +- Commit messages should be short (~10 words), simple, and descriptive. + If it's too long, use multiple lines in the commit message +- Commits should be frequent. Whenever you reach a working state, you should commit before you accidentally break anything again +- Don't commit directly to the `main` branch, as `main` should **always** contain working code. + New code should be developed on separate branches and can only be merged through a pull request after being reviewed and approved. +- **ALWAYS ALWAYS ALWAYS** commit and push before you leave a meeting, especially if you are using a computer that is not yours! + It is never fun to have to commit someone else's code at the start of the day or find out an hour in that you've been working off of someone else's uncommitted (potentially broken!) code. + Uncommitted code also makes it harder to track what is and isn't finished. +- Run a `git status` at the start of a meeting to make sure you committed your code and that you are on the right branch + +### Names diff --git a/mkdocs.yml b/mkdocs.yml index b4cbfe0..da96b62 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,7 @@ nav: - 'basics/sensors.md' - 'basics/wpilib.md' - 'basics/java_basics.md' + - 'basics/ElectronicsCrashCourse.md' - 'basics/vscode_tips.md' # - 'basics/driverstation_tips.md' - FRC Development Environment Setup: @@ -32,14 +33,15 @@ nav: - 'examples/pid_shooter.md' - Version Control: - 'version_control/github.md' + - 'version_control/BasicGit.md' - Improve the Documentation: - 'contributing.md' # Setup site_name: FRC Java Programming repo_name: Tutorial Bot -repo_url: https://github.com/FRCTeam3255/FRC-Java-Tutorial/tree/main -edit_uri: https://github.com/FRCTeam3255/FRC-Java-Tutorial/edit/main/Docs_Source/docs/ +repo_url: https://github.com/RoboLancers/FRC-Java-Tutorial/tree/main +edit_uri: https://github.com/RoboLancers/FRC-Java-Tutorial/edit/main/Docs_Source/docs/ site_author: Tayler Uva # Theme