├── CNAME ├── .gitignore ├── src ├── misc │ ├── why_kotlin │ │ ├── ConstructorParams.kt │ │ ├── DefaultValues.kt │ │ ├── DefaultValues.java │ │ ├── VarsAndVals.kt │ │ ├── ConstructorParams.java │ │ ├── VarsAndVals.java │ │ └── why_kotlin.md │ ├── terminology_and_acronyms.md │ └── pedro_vs_roadrunner.md ├── static │ ├── intro_to_git │ │ ├── fork.png │ │ ├── branches.png │ │ ├── git_menu.png │ │ ├── commit menu.png │ │ ├── get_from_vcs.png │ │ ├── pull_request.png │ │ ├── push_button.png │ │ ├── update_project.png │ │ ├── personal_access_token.png │ │ └── merge_conflicts_dialog_dark.png │ ├── builder_reference_videos │ │ ├── turn().mp4 │ │ ├── turnTo().mp4 │ │ ├── lineToX().mp4 │ │ ├── lineToY().mp4 │ │ ├── splineTo().mp4 │ │ ├── strafeTo().mp4 │ │ ├── waitSeconds().mp4 │ │ ├── setReversed(false).mp4 │ │ ├── setReversed(true).mp4 │ │ ├── splineTo() (default).mp4 │ │ ├── splineToLinearHeading().mp4 │ │ ├── splineToSplineHeading().mp4 │ │ ├── strafeToLinearHeading().mp4 │ │ ├── strafeToSplineHeading().mp4 │ │ └── splineToConstantHeading().mp4 │ ├── vref_small_v0_high_rr_10 │ │ └── badgraph.webp │ ├── is_the_bump_on_manual_feedforward_tuner_normal │ │ └── deceleration_bump.png │ └── robot_velocity_plateaus_below_target_velocity_plateau │ │ └── lowPlateau.png ├── gradle │ ├── dont_upgrade │ │ ├── upgrade_agp_message.png │ │ ├── agp_version[GTE10.1.1].png │ │ ├── agp_version[LT10.1.1].png │ │ ├── as_file_explorer_modes.png │ │ ├── gradle_wrapper_version[LT10.1.1].png │ │ ├── gradle_wrapper_version[GTE10.1.1].png │ │ └── dont_upgrade.md │ ├── downgrading_gradle_jdk │ │ ├── as_gradle_settings.png │ │ ├── failed_gradle_build.png │ │ └── downgrading_gradle_jdk.md │ └── project_templates │ │ └── project_templates.md ├── common_ds_errors │ └── npe_at_init │ │ ├── npe_exception.jpg │ │ └── npe_at_init.md ├── roadrunner_056 │ ├── robot_drifts_to_one_side_during_manual_feedforward_tuning.md │ ├── robot_velocity_plateaus_below_target_velocity_plateau.md │ ├── manual_feedforward_tuner_overshooots.md │ ├── is_the_bump_on_manual_feedforward_tuner_normal.md │ ├── robot_drives_full_speed_on_start_when_following_trajectory.md │ ├── robot_drifts_while_tuning_follower_pid.md │ ├── manual_feed_forward_tuner_opposite_velocities.md │ ├── robot_localization_makes_circle_when_spinning_in_place.md │ └── how_to_integrate_a_PIDF_controller_with_roadrunner │ │ ├── RoadRunnerPIDFSF.java │ │ ├── RoadRunnerPIDF.java │ │ └── how_to_integrate_a_PIDF_controller_with_roadrunner.md ├── improving_loop_times │ ├── MeasuringLoopTimes.java │ ├── CachingOptimizedOpMode.java │ └── improving_loop_times.md ├── roadrunner_10 │ ├── null_list_error_in_rr_10.md │ ├── vref_small_v0_high_rr_10.md │ ├── BuilderReference.java │ └── complete_trajectorybuilder_reference.md ├── introduction.md ├── electrical │ ├── why_we_should_only_use_usb_30.md │ └── how_to_wire_odometry_pods.md ├── pidf_controllers │ ├── syncing_two_linear_slide_motors_using_a_pidf_controller │ │ ├── PIDFExample.java │ │ └── syncing_two_linear_slide_motors_using_a_pidf_controller.md │ └── integrating_a_custom_PIDF_controller.md ├── contributors.md ├── SUMMARY.md ├── tags.md └── intro_to_programming │ ├── setup.md │ └── intro_to_git.md ├── .idea ├── .gitignore ├── vcs.xml ├── git_toolbox_blame.xml ├── modules.xml ├── cookbook.iml └── inspectionProfiles │ └── Project_Default.xml ├── readme.md ├── LICENSE ├── .github └── workflows │ └── deploy.yml ├── contributing.md ├── book.toml └── theme └── index.hbs /CNAME: -------------------------------------------------------------------------------- 1 | cookbook.dairy.foundation 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | book/* 3 | 4 | .DS_Store 5 | **/.DS_Store -------------------------------------------------------------------------------- /src/misc/why_kotlin/ConstructorParams.kt: -------------------------------------------------------------------------------- 1 | class ConstructorParams (val val1: String, var var1: Int) -------------------------------------------------------------------------------- /src/static/intro_to_git/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/fork.png -------------------------------------------------------------------------------- /src/misc/why_kotlin/DefaultValues.kt: -------------------------------------------------------------------------------- 1 | class DefaultValues { 2 | fun function(arg: Int = 0) { 3 | val a = arg + 10 4 | } 5 | } -------------------------------------------------------------------------------- /src/static/intro_to_git/branches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/branches.png -------------------------------------------------------------------------------- /src/static/intro_to_git/git_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/git_menu.png -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /src/static/intro_to_git/commit menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/commit menu.png -------------------------------------------------------------------------------- /src/static/intro_to_git/get_from_vcs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/get_from_vcs.png -------------------------------------------------------------------------------- /src/static/intro_to_git/pull_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/pull_request.png -------------------------------------------------------------------------------- /src/static/intro_to_git/push_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/push_button.png -------------------------------------------------------------------------------- /src/static/intro_to_git/update_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/update_project.png -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/upgrade_agp_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/dont_upgrade/upgrade_agp_message.png -------------------------------------------------------------------------------- /src/static/builder_reference_videos/turn().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/turn().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/turnTo().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/turnTo().mp4 -------------------------------------------------------------------------------- /src/common_ds_errors/npe_at_init/npe_exception.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/common_ds_errors/npe_at_init/npe_exception.jpg -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/agp_version[GTE10.1.1].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/dont_upgrade/agp_version[GTE10.1.1].png -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/agp_version[LT10.1.1].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/dont_upgrade/agp_version[LT10.1.1].png -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/as_file_explorer_modes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/dont_upgrade/as_file_explorer_modes.png -------------------------------------------------------------------------------- /src/static/builder_reference_videos/lineToX().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/lineToX().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/lineToY().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/lineToY().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/splineTo().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/splineTo().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/strafeTo().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/strafeTo().mp4 -------------------------------------------------------------------------------- /src/static/intro_to_git/personal_access_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/personal_access_token.png -------------------------------------------------------------------------------- /src/static/vref_small_v0_high_rr_10/badgraph.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/vref_small_v0_high_rr_10/badgraph.webp -------------------------------------------------------------------------------- /src/static/builder_reference_videos/waitSeconds().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/waitSeconds().mp4 -------------------------------------------------------------------------------- /src/static/intro_to_git/merge_conflicts_dialog_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/intro_to_git/merge_conflicts_dialog_dark.png -------------------------------------------------------------------------------- /src/gradle/downgrading_gradle_jdk/as_gradle_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/downgrading_gradle_jdk/as_gradle_settings.png -------------------------------------------------------------------------------- /src/gradle/downgrading_gradle_jdk/failed_gradle_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/downgrading_gradle_jdk/failed_gradle_build.png -------------------------------------------------------------------------------- /src/static/builder_reference_videos/setReversed(false).mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/setReversed(false).mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/setReversed(true).mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/setReversed(true).mp4 -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/gradle_wrapper_version[LT10.1.1].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/dont_upgrade/gradle_wrapper_version[LT10.1.1].png -------------------------------------------------------------------------------- /src/static/builder_reference_videos/splineTo() (default).mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/splineTo() (default).mp4 -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/gradle_wrapper_version[GTE10.1.1].png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/gradle/dont_upgrade/gradle_wrapper_version[GTE10.1.1].png -------------------------------------------------------------------------------- /src/static/builder_reference_videos/splineToLinearHeading().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/splineToLinearHeading().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/splineToSplineHeading().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/splineToSplineHeading().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/strafeToLinearHeading().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/strafeToLinearHeading().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/strafeToSplineHeading().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/strafeToSplineHeading().mp4 -------------------------------------------------------------------------------- /src/static/builder_reference_videos/splineToConstantHeading().mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/builder_reference_videos/splineToConstantHeading().mp4 -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_blame.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/misc/why_kotlin/DefaultValues.java: -------------------------------------------------------------------------------- 1 | public class DefaultValues { 2 | public void function(int arg) { 3 | int a = arg + 10; 4 | } 5 | 6 | public void function() { 7 | function(0); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/static/is_the_bump_on_manual_feedforward_tuner_normal/deceleration_bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/is_the_bump_on_manual_feedforward_tuner_normal/deceleration_bump.png -------------------------------------------------------------------------------- /src/static/robot_velocity_plateaus_below_target_velocity_plateau/lowPlateau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dr-hextanium/cookbook/HEAD/src/static/robot_velocity_plateaus_below_target_velocity_plateau/lowPlateau.png -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/misc/why_kotlin/VarsAndVals.kt: -------------------------------------------------------------------------------- 1 | class VarsAndVals { 2 | var var1 = 0 3 | private var var2 = "variable string" 4 | 5 | val val1 = 0 6 | private val val2 = "value string" 7 | 8 | fun getting() { 9 | val local1 = var1 10 | val local2 = var2 11 | 12 | val local3 = val1 13 | val local4 = val2 14 | } 15 | 16 | fun setting() { 17 | var1 = 100 18 | var2 = "new value" 19 | } 20 | } -------------------------------------------------------------------------------- /src/roadrunner_056/robot_drifts_to_one_side_during_manual_feedforward_tuning.md: -------------------------------------------------------------------------------- 1 | # Robot Drifts to One Side During Manual Feedforward Tuning 2 | 3 | If this happens, you shouldn't worry. 4 | This can be caused by many reasons, such as an unbalanced robot or one wheel having slightly more friction than the others. 5 | 6 | Whatever the reason, this will be corrected for in the later tuning steps. 7 | It can safely be ignored. 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Cookbook 2 | A clean, quick reference for common issues faced in the context of the FIRST Tech Challenge. 3 | 4 | ## Get in touch 5 | Join our [Discord server](https://discord.gg/7KwsaztygU), or reach out to [me](https://discord.com/users/280024224121356288) directly. 6 | 7 | ## Contributing 8 | Check out [the contributing page.](./contributing.md) and the [GitHub](https://github.com/dr-hextanium/cookbook/). 9 | 10 | -------------------------------------------------------------------------------- /src/misc/why_kotlin/ConstructorParams.java: -------------------------------------------------------------------------------- 1 | public class ConstructorParams { 2 | private final String val1; 3 | private int var1; 4 | 5 | public ConstructorParams(String val1, int var1) { 6 | this.val1 = val1; 7 | this.var1 = var1; 8 | } 9 | 10 | public int getVar1() { 11 | return var1; 12 | } 13 | 14 | public String getVal1() { 15 | return val1; 16 | } 17 | 18 | public void setVar1(int var1) { 19 | this.var1 = var1; 20 | } 21 | } -------------------------------------------------------------------------------- /src/roadrunner_056/robot_velocity_plateaus_below_target_velocity_plateau.md: -------------------------------------------------------------------------------- 1 | # Robot Velocity Plateaus Below Target Velocity Plateaus 2 | 3 | ![image of robot velocity not reaching max target velocity](../static/robot_velocity_plateaus_below_target_velocity_plateau/lowPlateau.png) 4 | 5 | This means you've reached your robot's actual max velocity. 6 | You should lower the max velocity specified in DriveConstants. 7 | Run the MaxVelocityTuner to find the recommended max velocity to use. 8 | -------------------------------------------------------------------------------- /src/roadrunner_056/manual_feedforward_tuner_overshooots.md: -------------------------------------------------------------------------------- 1 | # Manual Feedforward Tuner Overshoots 2 | 3 | This is normal! 4 | The REV hub motor controllers are not great at decelerating, so this typically causes about a 10% overshoot on manual feedforward tuner. 5 | It is okay to move on to the next tuning steps. 6 | 7 | However, when you get to the feedback tuning, whether it's "Back and Forth" or "FollowerPIDTuner," you will want to add a non-zero kD term. 8 | This will help the robot not overshoot. 9 | -------------------------------------------------------------------------------- /.idea/cookbook.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.md: -------------------------------------------------------------------------------- 1 | # Is the Bump On Manual Feedforward Tuner Normal? 2 | 3 | ![image of deceleration bump](../static/is_the_bump_on_manual_feedforward_tuner_normal/deceleration_bump.png) 4 | 5 | Yes! 6 | The bump when accelerating and decelerating is normal. 7 | It is caused by a fundamental hardware issue with the Control and Expansion Hubs that makes deceleration weird. 8 | There's nothing you can do about it; just try to get the plates and slopes to match up as closely as possible. 9 | -------------------------------------------------------------------------------- /src/improving_loop_times/MeasuringLoopTimes.java: -------------------------------------------------------------------------------- 1 | package org.firstinspires.ftc.teamcode; 2 | 3 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 4 | import com.qualcomm.robotcore.util.ElapsedTime; 5 | 6 | public class MeasuringLoopTimes extends OpMode { 7 | private ElapsedTime elapsedtime; 8 | 9 | @Override 10 | public void init() { 11 | elapsedtime = new ElapsedTime(); 12 | elapsedtime.reset(); 13 | } 14 | 15 | @Override 16 | public void loop() { 17 | 18 | telemetry.addData("Loop Times", elapsedtime.milliseconds()); 19 | elapsedtime.reset(); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/roadrunner_056/robot_drives_full_speed_on_start_when_following_trajectory.md: -------------------------------------------------------------------------------- 1 | # Robot Drive Full Speed on Start When Following Trajectory 2 | 3 | If you are running an OpMode that has Roadrunner trajectories in it, and when you start moving it goes at full speed right away, this almost always means you forgot to set a pose estimate. 4 | In `init()`, before you run any trajectories, make sure you have `drive.setPoseEstimate(startingPose)`, whatever your starting pose may be. 5 | Make sure that the trajectory starting pose matches this: 6 | ```java 7 | Trajectory traj = drive.trajectoryBuilder(startingPose) 8 | ... 9 | .build(); 10 | ``` 11 | -------------------------------------------------------------------------------- /src/roadrunner_10/null_list_error_in_rr_10.md: -------------------------------------------------------------------------------- 1 | # Null list error in Road Runner 1.0 2 | 3 | ### Ingredients 4 | 5 | 1. A Road Runner 1.0 setup 6 | 2. Completed ForwardPushTest and LateralPushTest 7 | 8 | 9 | ## The Recipe 10 | 11 | ### The problem 12 | 13 | If you have gotten through Road Runner 1.0 tuning to the ForwardRampLogger tuning step (you may also see this in LateralRampLogger or AngularRampLogger), sometimes you will get an empty list error when you press the "latest" button. 14 | 15 | ### Solution 16 | You must first run the OpMode from the Driver Station and then stop it once the robot's speed stops increasing. 17 | Finally, you can open the tuning page on your robot's Wi-Fi network, as the Road Runner docs say. 18 | -------------------------------------------------------------------------------- /src/roadrunner_056/robot_drifts_while_tuning_follower_pid.md: -------------------------------------------------------------------------------- 1 | # Robot Drifts While Tuning Follower PID 2 | 3 | ### Check Localization 4 | Run `LocalizationTest` and drive the robot back and forth a few times. 5 | You want to ensure that the behavior shown on the dashboard mirrors that of what you can see. 6 | If the robot was veering in `BackAndForth`, see if the dashboard bot is veering the same. 7 | If you notice the same discrepancy while running `LocalizationTest`, it means the problem is in your localization. 8 | 9 | ### Tuning PID 10 | If you're certain that localization works fine and the robot "knows" that it's wrong, but isn't correcting, then you need to tune your PID values more. 11 | You can tune your controller through the steps on [this page of Game Manual 0](https://gm0.org/en/latest/docs/software/concepts/control-loops.html#tuning-a-pid-loop). 12 | -------------------------------------------------------------------------------- /src/introduction.md: -------------------------------------------------------------------------------- 1 | # introduction 2 | 3 | Welcome to the Cookbook. Enjoy your stay :) 4 | 5 | The Cookbook is essentially a "handbook" maintained by the community for common (or not) issues that people have faced. The original motivation for this was to better prepare my team (4017) for when I left, since we've gained so much knowledge over just a single year that may eventually be lost. Throughout our region, I have seen many teams be completely lost after a great programmer or designer leaves, because they were simply not taught. 6 | 7 | While a list of problems and solutions is not "teaching one how to fish", it serves as an incredibly helpful guide to just get things working. I hope by making my lil' cookbook open source, **everyone** can both use and build on my limited knowledge base. 8 | 9 | If you (or anyone) has a problem, I'd love if you could make a lil' "recipe" and help anyone else in the future :) -------------------------------------------------------------------------------- /src/misc/why_kotlin/VarsAndVals.java: -------------------------------------------------------------------------------- 1 | public class VarsAndVals { 2 | private int var1 = 0; 3 | private String var2 = "variable string"; 4 | 5 | private final int val1 = 0; 6 | private final String val2 = "value string"; 7 | 8 | public int getVar1() { 9 | return var1; 10 | } 11 | 12 | public void setVar1(int var1) { 13 | this.var1 = var1; 14 | } 15 | 16 | private String getVar2() { 17 | return var2; 18 | } 19 | 20 | private void setVar2(String string) { 21 | this.var2 = string; 22 | } 23 | 24 | public int getVal1() { 25 | return val1; 26 | } 27 | 28 | private String getVar2() { 29 | return var2; 30 | } 31 | 32 | public void getting() { 33 | final int local1 = getVar1(); 34 | final String local2 = getVar2(); 35 | 36 | final int local3 = getVal1(); 37 | final String local4 = getVal2(); 38 | } 39 | 40 | public void setting() { 41 | setVar1(100); 42 | setVar2("new value"); 43 | } 44 | } -------------------------------------------------------------------------------- /src/gradle/project_templates/project_templates.md: -------------------------------------------------------------------------------- 1 | # Alternative Project Templates 2 | 3 | Dairy hosts a series of plugins and templates that use them in order to simplify 4 | your TeamCode project structure. 5 | 6 | [Dairy-Foundation/Templates](https://github.com/Dairy-Foundation/Templates/) 7 | 8 | Advantages: 9 | - Gradle configuration is managed by a plugin, its easier to update the project 10 | outside of gradle version changes. 11 | - Supports easily managing all of the SDK as one version. 12 | - Supports easily adding Kotlin support to your project. 13 | - Will support easily adding more FTC Libraries in the future. 14 | - Supports building Library modules along with TeamCode modules. 15 | - Simplified Project setup. 16 | 17 | Disadvantages: 18 | - Currently under documented. 19 | - Not the official SDK setup, which may be confusing for inexperienced team 20 | members. 21 | - Needs more examples and templates for more complex setups. 22 | - Does not yet support many common FTC Libraries. 23 | -------------------------------------------------------------------------------- /src/electrical/why_we_should_only_use_usb_30.md: -------------------------------------------------------------------------------- 1 | # Why to only use USB 3.0 2 | 3 | 4 | ### Ingredients 5 | 6 | 7 | 1. A Control Hub 8 | 2. At least 1 USB device 9 | 3. A USB hub (optional) 10 | 11 | 12 | ## The Recipe 13 | 14 | 15 | ### Overview 16 | The Control Hub has a lot of nuances that many people do not know of, including the dangers of the onboard USB 2.0 port. 17 | 18 | ### The Problem 19 | The Control Hub's USB 2.0 port shares a ground with the Wi-Fi chipset on the Control Hub. 20 | This provides a path for a static shock to the USB device to cause a Wi-Fi disconnect mid-match. 21 | 22 | 23 | ### How to Mitigate the Problem 24 | The best way to mitigate the problem is to not use the USB 2.0 port on your Control Hub. 25 | For teams with no or only one USB device, that's not a problem, just use the USB 3.0 port instead of the USB 2.0 port. 26 | If your team needs more than one device, such as an Expansion Hub over USB *and* a USB camera for object detection, it gets more complicated. 27 | To prevent shock, you can get a USB hub and connect all devices through just the USB 3.0 port. 28 | -------------------------------------------------------------------------------- /src/roadrunner_056/manual_feed_forward_tuner_opposite_velocities.md: -------------------------------------------------------------------------------- 1 | # Target Velocity is Positive When Measured Velocity is Negative When Tuning Manual Feedforward 2 | 3 | If MotorDirectionDebugger works perfectly, this means that either your right side encoders are plugged in to the wrong ports (so swap `frontRight` and `backRight` encoder cables) or your left side encoders are plugged in to the wrong ports (so swap `frontLeft` and `backLeft` encoder cables). 4 | An easy way to debug this is to add a `printEncoderValues` telemetry method in `SampleMecanumDrive`. 5 | 6 | ```java 7 | public void printEncoderValues(Telemetry telemetry) { 8 | telemetry.addData("LeftFrontPos: ", leftFront.getCurrentPosition()); 9 | telemetry.addData("RightFrontPos: ", rightFront.getCurrentPosition()); 10 | telemetry.addData("LeftRearPos: ", leftRear.getCurrentPosition()); 11 | telemetry.addData("RightBackPos: ", rightRear.getCurrentPosition()); 12 | } 13 | ``` 14 | Then at the end of every loop in MotorDirectionDebugger, call 15 | ```java 16 | drive.printEncoderValues(telemetry); 17 | ``` 18 | -------------------------------------------------------------------------------- /src/roadrunner_056/robot_localization_makes_circle_when_spinning_in_place.md: -------------------------------------------------------------------------------- 1 | # Robot Localization Makes Circle When Spinning In Place 2 | 3 | So when you spin the robot in place, the drawing on FTC Dashboard is making a circle. 4 | This is normal and can be fixed. 5 | 6 | ### What causes it? 7 | When the robot is spun, the strafing odometry wheel moves which makes the localization think the robot moved in a circle. 8 | This can be counteracted using the forward distance from the strafing tracking wheel to the center of rotation. 9 | 10 | ### Three Wheel Solution 11 | For three wheel odometry, this means your forward offset isn't tuned correctly. 12 | Run the tuner and replace the value. 13 | If that doesn't work, check whether the strafing pod is closer to the front or the back of the robot. 14 | If it's closer to the back, the offset should be negative. 15 | 16 | ### Two Wheel Solution 17 | The solution for two wheel odometry is largely the same. 18 | Instead of the forward offset, you must tune the x and y position of the strafing pod. 19 | The same advice about positive and negative offset still applies. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Cookbook Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | -------------------------------------------------------------------------------- /src/roadrunner_10/vref_small_v0_high_rr_10.md: -------------------------------------------------------------------------------- 1 | # Vref small/V0 large/backwards in per tick in Roadrunner 1.0 2 | 3 | ### Ingredients 4 | 5 | 1. A Road Runner 1.0 setup 6 | 2. Running Manual Feedforward Tuner 7 | 8 | 9 | ## The Recipe 10 | 11 | ### The problem 12 | 13 | Sometimes, when you run the manual feedforward tuner, your robot will show ridiculously high values for v0 and v1. 14 | These numbers can often be in the hundreds of thousands. 15 | Something like this graph: 16 | 17 | ![image of graph with huge numbers](../static/vref_small_v0_high_rr_10/badgraph.webp) 18 | 19 | 20 | Another symptom of this issue is if the manual feedforward tuner barely moves at all. 21 | 22 | ### Solution 23 | The underlying cause of this issue is your inpertick value being a really large number. 24 | Inpertick should be a tiny number, significantly less than one; 25 | when running forward push test, you must divide the number of inches pushed by the number of ticks recorded, not the other way around. 26 | 27 | To fix this issue, you will need to run forward push test again, recalculate the correct number, and then redo your tuning from there. -------------------------------------------------------------------------------- /src/gradle/downgrading_gradle_jdk/downgrading_gradle_jdk.md: -------------------------------------------------------------------------------- 1 | # Downgrading the Gradle JDK on Android Studio Ladybug 2 | 3 | From version Ladybug | 2024.2.1 of Android Studio (AS), the software ships with 4 | Java 21 as the Gradle JDK. 5 | 6 |
7 | 8 | Trying to build an SDK project of version `10.1.1` or later without being on 9 | Android Studio Ladybug or later will not work. 10 | 11 |
12 | 13 | This causes build issues for FTC projects on SDK versions before `10.1.1`. 14 | 15 | The error looks like this: 16 | ![erroring build](./failed_gradle_build.png) 17 | 18 | Although its tempting to press one of those magical blue links, this is a 19 | horrible idea. 20 | 21 | The correct fix here is to downgrade the Gradle JDK version to 17, which is 22 | fairly easy to do. 23 | 24 | ![Android Studio Gradle settings menu](./as_gradle_settings.png) 25 | 26 | Select an option for Gradle JDK that is JDK version 17, and rebuild. 27 | 28 | If you have no JDK 17 available, then you can click the download JDK option to 29 | install JDK 17. 30 | 31 | JDK 17 is a good choice as it will also support the new builds for `10.1.1` 32 | onwards. 33 | 34 | -------------------------------------------------------------------------------- /src/pidf_controllers/syncing_two_linear_slide_motors_using_a_pidf_controller/PIDFExample.java: -------------------------------------------------------------------------------- 1 | public class PIDFExample extends LinearOpMode { 2 | 3 | // PID(F) declaration 4 | // kp = 0, ki = 0, kd = 0, kf = 0; 5 | 6 | private PIDFController examplePIDF = new PIDFController(0, 0, 0, 0); 7 | 8 | @Override 9 | public void runOpMode() { 10 | int targetPosition = 0; 11 | 12 | // motor setup 13 | DcMotorEx leftSlide = hardwareMap.get(DcMotorEx.class, "leftSlide"); 14 | DcMotorEx rightSlide = hardwareMap.get(DcMotorEx.class, "rightSlide"); 15 | 16 | waitForStart(); 17 | 18 | while(opModeIsActive()) { 19 | /* 20 | Calculates PID based only on one encoder. 21 | This can also be the average position of the two linear slides, but we haven't noticed much difference 22 | */ 23 | double power = PIDFController.update(leftSlide.getCurrentPosition(), targetPosition); 24 | 25 | // see how both motors are getting the same power 26 | leftSlide.setPower(power); 27 | rightSlide.setPower(power); 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/contributors.md: -------------------------------------------------------------------------------- 1 | # contributors 2 | - hex [(github)](https://github.com/dr-hextanium) [(discord)](https://discord.com/users/280024224121356288) 3 | - 0verkil [(github)](https://github.com/0verkil) [(discord)](https://discord.com/users/671902633430089748) 4 | - Ruckus Robotics (14712) [(discord)](https://discord.com/users/292086403926589441) 5 | - JL [(github)](https://github.com/JoelLee3) [(discord)](https://discordapp.com/users/760523424635813980) 6 | - rjan939 [(github)](https://github.com/rjan939) [(discord)](https://discordapp.com/users/292725814556884995) 7 | - Froze 'n' Milk [(github)](https://github.com/Froze-N-Milk) 8 | - Iris [(github)](https://github.com/Iris-TheRainbow) [(discord)](https://discord.com/users/705965203807928381) 9 | - j5155 [(github)](https://github.com/j5155) [(discord)](https://discord.com/users/496774369054425109) 10 | - Arush [(github)](https://github.com/ArushYadlapati) [(discord)](https://discord.com/users/764258716463529986) 11 | - ZachWaffle [(github)](https://github.com/zachwaffle4) [(discord)](https://discord.com/users/522513851942436867) 12 | - Baron [(github)](https://github.com/BaronClaps) [(discord)](https://discord.com/users/813531426603270144) 13 | - Ishaan [(github)](https://github.com/ishaanko) 14 | - Davis [(github)](https://github.com/BeepBot99) [(discord)](https://discord.com/users/1217246652448903188) 15 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | -------------------------------------------------------------------------------- /src/improving_loop_times/CachingOptimizedOpMode.java: -------------------------------------------------------------------------------- 1 | package org.firstinspires.ftc.teamcode; 2 | 3 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 4 | import com.qualcomm.robotcore.hardware.DcMotorEx; 5 | import com.qualcomm.hardware.lynx.LynxModule; 6 | import com.qualcomm.robotcore.util.ElapsedTime; 7 | 8 | import java.util.List; 9 | 10 | import dev.frozenmilk.dairy.cachinghardware.CachingDcMotorEx; 11 | 12 | public class CachingOptimizedOpMode extends OpMode { 13 | 14 | private CachingDcMotorEx exampleMotor; 15 | private List allHubs; 16 | private ElapsedTime elapsedtime; 17 | 18 | @Override 19 | public void init() { 20 | 21 | elapsedtime = new ElapsedTime(); 22 | 23 | // this just sets the bulk reading mode for each hub 24 | allHubs = hardwareMap.getAll(LynxModule.class); 25 | for (LynxModule hub : allHubs) { 26 | hub.setBulkCachingMode(LynxModule.BulkCachingMode.MANUAL); 27 | } 28 | 29 | exampleMotor = new CachingDcMotorEx(hardwareMap.get(DcMotorEx.class, "example motor")); 30 | elapsedtime.reset(); 31 | } 32 | 33 | @Override 34 | public void loop() { 35 | // clears the cache on each hub 36 | for (LynxModule hub : allHubs) { 37 | hub.clearBulkCache(); 38 | } 39 | 40 | // after the first time, it won't actually send new commands 41 | exampleMotor.setPower(1); 42 | 43 | telemetry.addData("Motor Position", exampleMotor.getCurrentPosition()); 44 | telemetry.addData("Loop Times", elapsedtime.milliseconds()); 45 | elapsedtime.reset(); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/pidf_controllers/syncing_two_linear_slide_motors_using_a_pidf_controller/syncing_two_linear_slide_motors_using_a_pidf_controller.md: -------------------------------------------------------------------------------- 1 | # Syncing Two Linear Slide Motors Using a PIDF Controller 2 | 3 | ### Ingredients 4 | 5 | 1. A PID(F) controller 6 | 2. Tuned PID(F) coefficients 7 | 8 | ## The Recipe 9 | 10 | ### The Problem 11 | 12 | Linear slides powered by two different motors can end up twisted, with one slide higher than the other. 13 | This can happen if the two encoders get out of sync. 14 | If you are using the RUN_TO_POSITION motor mode, this causes one slide to be supplied more power than the other. 15 | Even if you are using a custom PID(F) controller on each motor, the same problem would occur. 16 | 17 | ### Methodology 18 | 19 | **The BEST way to keep these in sync is to have them be mechanically connected with a bar or a piece of channel. 20 | If the two act as one rigid body, then it is a lot less dependent on software. 21 | However, this is not always possible, hence the software solutions.** 22 | 23 | Instead, the way this recipe will explain is with a leader and a follower linear slide motor. 24 | This means we will use a PID(F) controller on one of the linear slide positions and just set that power to both motors. 25 | This resolves the issue of the two linear slides being supplied different powers. 26 | If both motors are going the same speed and both linear slides are well tensioned, the linear slides should stay synced. 27 | 28 | ### Code Example 29 | 30 | This code example is going to assume you have a working PID(F) controller class and tuned coefficients. 31 | 32 | ```java 33 | {{#rustdoc_include PIDFExample.java:3:29}} 34 | ``` 35 | 36 | 37 | *Last Updated: 2024-05-29* -------------------------------------------------------------------------------- /src/electrical/how_to_wire_odometry_pods.md: -------------------------------------------------------------------------------- 1 | # How to wire odometry pods 2 | 3 | ### Ingredients 4 | 5 | 6 | 1. An Expansion Hub or Control Hub 7 | 2. either 2 or 3 odometry pods/modules 8 | 9 | ## The Recipe 10 | 11 | ### The problem 12 | The Rev Control Hub and Expansion Hub, as found [here](https://blog.eeshwark.com/robotblog/rev-hub-quadrature) by 7244 alum Eeshwar, only have 2 reliable quadrature encoder ports. 13 | This means high CPR/PPR encoders such as the Rev Through Bore encoder will miss counts on ports 1 and 2 which will lead to drift. 14 | 15 | 16 | ### Solutions 17 | ### 2 Wheel Odometry 18 | For teams that use a 2 wheel + IMU setup, the solution is simple! 19 | Put the drive motors on the Control Hub. 20 | Then, as you don't need drive encoders, you can simply put the odometry on Control Hub encoder ports 0 and 3 where you would usually put the motor encoders. 21 | 22 | ### 3 Wheel Odometry 23 | For teams with 3 wheel odometry, it is a bit more complex. 24 | The most important odometry pods are the parallel ones since encoder drift with them will cause heading drift. 25 | This can rapidly ruin your localization as heading is used as a basis for all other measurements. 26 | Since they're the most important, the parallel pods should go in ports 0 and 3 on the Control Hub. 27 | The perpendicular (strafe) pod is less important to localization, so it is fine to put it in port 1 or 2 on the Control Hub. 28 | 29 | Note that you should always put odometry on the Control Hub (or at least all on the same hub) even if you must place it in ports 1 or 2. 30 | This is because reading from the Expansion Hub requires an additional bulk read. 31 | This can greatly worsen loop times and is not worth the benefits of using the 0 and 3 ports. 32 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write # To push a branch 12 | pull-requests: write # To create a PR from that branch 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - name: Install latest mdbook 18 | run: | 19 | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') 20 | url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" 21 | mkdir mdbook 22 | curl -sSL $url | tar -xz --directory=./mdbook 23 | echo `pwd`/mdbook >> $GITHUB_PATH 24 | - name: Install latest mdbook-blame 25 | run: | 26 | tag=$(curl 'https://api.github.com/repos/Froze-N-Milk/mdbook-blame/releases/latest' | jq -r '.tag_name') 27 | url="https://github.com/Froze-N-Milk/mdbook-blame/releases/download/${tag}/mdbook-blame-${tag}-x86_64-unknown-linux-gnu.tar.gz" 28 | mkdir mdbook-blame 29 | curl -sSL $url | tar -xz --directory=./mdbook-blame 30 | echo `pwd`/mdbook-blame >> $GITHUB_PATH 31 | - name: Deploy GitHub Pages 32 | run: | 33 | # This assumes your book is in the root of your repository. 34 | # Just add a `cd` here if you need to change to another directory. 35 | mdbook build 36 | git worktree add gh-pages 37 | git config user.name "Deploy from CI" 38 | git config user.email "" 39 | cd gh-pages 40 | # Delete the ref to avoid keeping history. 41 | git update-ref -d refs/heads/gh-pages 42 | rm -rf * 43 | mv ../book/* . 44 | mv ../CNAME . 45 | git add . 46 | git commit -m "Deploy $GITHUB_SHA to gh-pages" 47 | git push --force --set-upstream origin gh-pages 48 | -------------------------------------------------------------------------------- /src/roadrunner_056/how_to_integrate_a_PIDF_controller_with_roadrunner/RoadRunnerPIDFSF.java: -------------------------------------------------------------------------------- 1 | public class RoadRunnerPIDFSF extends LinearOpMode { 2 | 3 | public enum STATES { 4 | INIT, 5 | DRIVE_FORWARD, 6 | STRAFE_LEFT_AND_LIFT_SLIDES, 7 | DRIVE_BACKWARD, 8 | STOP; 9 | } 10 | 11 | SampleMecanumDrive drive; 12 | DcMotorEx linearSlides; 13 | PIDFController PIDF; 14 | int targetPosition = 0; 15 | 16 | public void runOpMode() { 17 | 18 | // all the same initialization and trajectory building as above 19 | 20 | StateMachine machine = new StateMachine() 21 | .state(STATES.INIT) // creates a new state 22 | .transition(() -> isStarted()) // condition to transition from this state to the next one 23 | 24 | .state(STATES.DRIVE_FORWARD) // register a new state 25 | .onEnter(() -> drive.followTrajectorySequenceAsync(forward)) // code to happen one time when entering this state 26 | .transition(() -> !drive.isBusy()) 27 | 28 | .state(STATES.STRAFE_LEFT_AND_LIFT_SLIDES) 29 | .onEnter(() -> drive.followTrajectorySequenceAsync(strafeLeft)) 30 | .transition(() -> (!drive.isBusy() && linearSlides.atTarget())) 31 | 32 | .state(STATES.DRIVE_BACKWARD) 33 | .onEnter(() -> drive.followTrajectorySequenceAsync(backward)) 34 | .transition(() -> !drive.isBusy()) 35 | 36 | .state(STATES.STOP) 37 | 38 | .build(); 39 | // building this StateMachine doesn't actually do anything. We still need to run it 40 | 41 | waitForStart(); 42 | 43 | machine.start(); // this starts the state machine, putting us into the first state 44 | 45 | while(opModeIsActive()) { 46 | machine.update(); 47 | drive.update(); 48 | double power = PIDF(linearSlides.getCurrentPosition(), targetPosition); 49 | linearSlides.setPower(power); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/gradle/dont_upgrade/dont_upgrade.md: -------------------------------------------------------------------------------- 1 | # Don't upgrade the Gradle version or Android Gradle Plugin version 2 | 3 | > Consider checking out [our article on alternate project 4 | > setups](../project_templates/project_templates.md) 5 | 6 | Android studio loves to tempt you with this little pop up, prompting you to 7 | upgrade the Android Gradle Plugin (AGP). 8 | 9 | ![Android Studio upgrade AGP message](./upgrade_agp_message.png) 10 | 11 | You need to click on [More] > Don't show again for this project. This will 12 | prevent the issue from occurring again. 13 | 14 | These upgrades are not productive, and interfere with the current gradle build, 15 | even if you get it working on your computer, it may cause issues with team 16 | member's computers, and it makes it harder to upgrade the SDK when a new version 17 | releases. Additionally, it may cause issues when working with other gradle 18 | operations, like adding libraries. 19 | 20 | If you accidentally upgrade the AGP or the Gradle version, you should hopefully 21 | be able to use git to undo the change, or, you can override those files with 22 | ones from the SDK. 23 | 24 | It may be possible to undo the changes more simply by downgrading again. 25 | 26 | > It may be easier to find these files with the file explorer set to `Project` 27 | mode, rather than `Android` mode. 28 | ![AS file explorer modes](./as_file_explorer_modes.png) 29 | 30 | ## `10.1.1` onwards 31 | 32 | The AGP version should be `8.7.0`. It is set in the project root `build.gradle`. 33 | ![AGP version](./agp_version[GTE10.1.1].png) 34 | 35 | The Gradle version should be `8.9`. It is set in `gradle/wrapper/gradle-wrapper.properties`. 36 | ![Gradle version](./gradle_wrapper_version[GTE10.1.1].png) 37 | 38 | ## Before `10.1.1` 39 | 40 | The AGP version should be `7.2.0`. It is set in the project root `build.gradle`. 41 | ![AGP version](./agp_version[LT10.1.1].png) 42 | 43 | The Gradle version should be `7.4.2`. It is set in `gradle/wrapper/gradle-wrapper.properties`. 44 | ![Gradle version](./gradle_wrapper_version[LT10.1.1].png) 45 | -------------------------------------------------------------------------------- /src/misc/terminology_and_acronyms.md: -------------------------------------------------------------------------------- 1 | # Acronyms 2 | 3 | ## Hardware and Electronics 4 | **CHub**: Control Hub
5 | **EHub/ExHub**: Expansion Hub
6 | **DS**: Driver Station
7 | **RC**: Robot Controller
8 | **ESD**: Electrostatic Discharge
9 | **DC**: Disconnect 10 | 11 | ## SDK Built-In 12 | **SDK**: [Standard Development Kit](https://github.com/FIRST-Tech-Challenge/FtcRobotController)
13 | **RTP**: RUN_TO_POSITION Motor Mode
14 | **RUE**: RUN_USING_ENCODERS Motor Mode
15 | **IMU**: [Internal Measurement Unit (Gyroscope)](https://ftc-docs.firstinspires.org/en/latest/programming_resources/imu/imu.html)
16 | **EOCV/OCV**: Easy OpenCV/OpenCV | [Vision Processor](https://ftc-docs.firstinspires.org/en/latest/apriltag/vision_portal/visionportal_overview/visionportal-overview.html), [Pipelines](https://github.com/OpenFTC/EasyOpenCV)
17 | **TFOD**: [TensorFlow Object Detection](https://ftc-docs.firstinspires.org/en/latest/programming_resources/index.html#tensorflow-programming)
18 | **hmap/hwmap**: HardwareMap 19 | 20 | ## Libraries 21 | **RR**: RoadRunner (Motion Planning Library) [0.5.6](https://learnroadrunner.com/), [1.0](https://rr.brott.dev/docs/v1-0/installation/) 22 | 23 | ## Controllers+ 24 | **Odo**: [Odometry](https://gm0.org/en/latest/docs/software/concepts/odometry.html?highlight=odometry)
25 | **MP**: [Motion Profiling](https://www.ctrlaltftc.com/advanced/motion-profiling)
26 | **FF**: [FeedForward](https://www.ctrlaltftc.com/feedforward-control)
27 | **PID(F)**: [Proportional Integral Derivative FeedForward Controller](https://www.ctrlaltftc.com/the-pid-controller)
28 | **KF/EKF**: [Kalman Filter / Extended Kalman Filter](https://www.ctrlaltftc.com/advanced/the-kalman-filter) 29 | 30 | ## Other 31 | **GH**: [GitHub](https://github.com/)
32 | **AS**: [Android Studio](https://developer.android.com/studio)
33 | **OBJ**: [OnBot Java](https://ftc-docs.firstinspires.org/en/latest/programming_resources/onbot_java/OnBot-Java-Tutorial.html)
34 | **ADB**: [Android Debug Bridge (used to wireless connect to control hub)](https://developer.android.com/tools/adb) 35 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # contributing 2 | 3 | ## purpose 4 | The Cookbook has one primary purpose: to serve as a handbook for common problems that beginners may have. 5 | It is **not** meant to be a replacement for [Game Manual 0](https://gm0.org/), which is much more comprehensive and tailored to informing. 6 | The cook book is primarily for questions and answers, although some guides that are more specific are permitted. 7 | 8 | ## how can i contribute? 9 | If you have the technical expertise to contribute, take a look at the next section. 10 | If not, but you really want a section or question to be answered/added, make a [GitHub issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-an-issue). 11 | 12 | ## recipe making 13 | 14 | Making a recipe is actually quite simple, all you need is a working knowledge of Git. 15 | 16 | 1. Make a [**fork**](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) 17 | of the [original repository](https://github.com/dr-hextanium/cookbook). 18 | 19 | 2. Add your content under `src`. Entries should: 20 | - be one Markdown (`.md`) file 21 | - have the `snake_case` version of the title as their file name 22 | - store any media (images, audios, etc.) they may have under a subfolder titled their filename in `src/static/` 23 | - have an appropriate `SUMMARY.md` entry 24 | - answer a question, address a common misconception, provide a solution for frequently asked questions 25 | - be structured thematically 26 | - begin with an h1 for the title (`#`) 27 | - have an h2 "Ingredients" section for requirements, if required 28 | - have a "last updated" field. 29 | 30 | 3. Add yourself to `contributors.md`, you deserve it :) 31 | 32 | 4. Commit and push your changes to your fork. 33 | - Make sure your commits abide by the (Conventional Commits)[https://www.conventionalcommits.org/en/v1.0.0/] standard. 34 | 35 | 5. Submit a pull request. 36 | - We'll have a little discussion on things you may have to change. 37 | - After everything is up to par, you will be accepted. 38 | 39 | 6. Star the repo <3 and live in peace knowing that you just helped somehow who had your exact problem. 40 | 41 | [This recipe](./src/integrating_a_custom_PIDF_controller.md) by Michael is a very well formatted recipe, and can serve as a reference/template. 42 | 43 | ## cookbook rendering 44 | The Cookbook is powered by [`mdbook`](https://rust-lang.github.io/mdBook/). 45 | To render the cookbook itself, take a look at their [command line instructions](https://rust-lang.github.io/mdBook/cli/index.html). 46 | 47 | Then, simply: 48 | ``` 49 | $ cd path/to/cookbook 50 | $ mdbook serve --open 51 | ``` 52 | 53 | Now, you will be able to view the book and your changes in real time, without reloading. -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["robopandas"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "The Cookbook" 7 | 8 | [preprocessor.blame] 9 | source = "https://github.com/dr-hextanium/cookbook/commit/" 10 | 11 | [output.html.code.hidelines] 12 | java = "# " 13 | kt = "# " 14 | 15 | [output.html] 16 | mathjax-support = true 17 | site-url = "/cookbook/" 18 | git-repository-url = "https://github.com/dr-hextanium/cookbook/tree/main" 19 | edit-url-template = "https://github.com/dr-hextanium/cookbook/tree/main/{path}" 20 | 21 | [output.html.redirect] 22 | "/help.html" = "/tags.html" 23 | "/as.html" = "/intro_to_programming/setup.html" 24 | "/git.html" = "/intro_to_programming/intro_to_git.html" 25 | "/jdk.html" = "/gradle/downgrading_gradle_jdk/downgrading_gradle_jdk.html" 26 | "/downgrade.html" = "/gradle/downgrading_gradle_jdk/downgrading_gradle_jdk.html" 27 | "/gradle.html" = "/gradle/dont_upgrade/dont_upgrade.html" 28 | "/emptylist.html" = "/roadrunner_10/null_list_error_in_rr_10.html" 29 | "/builder.html" = "/roadrunner_10/complete_trajectorybuilder_reference.html" 30 | "/bump.html" = "/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.html" 31 | "/ffbump.html" = "/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.html" 32 | "/overshoot.html" = "/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.html" 33 | "/null.html" = "/common_ds_errors/npe_at_init/npe_at_init.html" 34 | "/npe.html" = "/common_ds_errors/npe_at_init/npe_at_init.html" 35 | "/27971.html" = "/common_ds_errors/npe_at_init/npe_at_init.html" 36 | "/pid.html" = "/pidf_controllers/integrating_a_custom_PIDF_controller.html" 37 | "/pidf.html" = "/pidf_controllers/integrating_a_custom_PIDF_controller.html" 38 | "/pidsync.html" = "/pidf_controllers/syncing_two_linear_slide_motors_using_a_pidf_controller/syncing_two_linear_slide_motors_using_a_pidf_controller.html" 39 | "/usb.html" = "/electrical/why_we_should_only_use_usb_30.html" 40 | "/usb3.html" = "/electrical/why_we_should_only_use_usb_30.html" 41 | "/odo.html" = "electrical/how_to_wire_odometry_pods.html" 42 | "/kotlin.html" = "/misc/why_kotlin/why_kotlin.html" 43 | "/loop.html" = "/improving_loop_times/improving_loop_times.html" 44 | "/looptimes.html" = "/improving_loop_times/improving_loop_times.html" 45 | "/pedrovsrr.html" = "/misc/pedro_vs_roadrunner.html" 46 | "/pedro.html" = "/misc/pedro_vs_roadrunner.html" 47 | "/rrvspedro.html" = "/misc/pedro_vs_roadrunner.html" 48 | "/rrvpedro.html" = "/misc/pedro_vs_roadrunner.html" 49 | "/tab.html" = "/roadrunner_10/complete_trajectorybuilder_reference.html" 50 | "/trajectory.html" = "/roadrunner_10/complete_trajectorybuilder_reference.html" 51 | "/inpertick.html" = "/roadrunner_10/vref_small_v0_high_rr_10.html" 52 | "/tickperin.html" = "/roadrunner_10/vref_small_v0_high_rr_10.html" 53 | "/vref.html" = "/roadrunner_10/vref_small_v0_high_rr_10.html" -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # [Summary](https://rust-lang.github.io/mdBook/format/summary.html) 2 | 3 | [Introduction](./introduction.md) 4 | [Tags](./tags.md)] 5 | # Introduction to Programming 6 | - [Development Environment Setup](./intro_to_programming/setup.md) 7 | - [Introduction to Git and Github](./intro_to_programming/intro_to_git.md) 8 | 9 | # Gradle 10 | - [Downgrading the Gradle JDK on Android Studio Ladybug](./gradle/downgrading_gradle_jdk/downgrading_gradle_jdk.md) 11 | - [Don't upgrade the Gradle version or Android Gradle Plugin version](./gradle/dont_upgrade/dont_upgrade.md) 12 | - [Alternate Project Setups / Templates](./gradle/project_templates/project_templates.md) 13 | 14 | # Road Runner 1.0 15 | - [Empty list error in Road Runner 1.0](./roadrunner_10/null_list_error_in_rr_10.md) 16 | - [Complete TrajectoryBuilder Reference](./roadrunner_10/complete_trajectorybuilder_reference.md) 17 | - [Vref small/v0 high/backwards in per tick](./roadrunner_10/vref_small_v0_high_rr_10.md) 18 | 19 | # Road Runner 0.5.6 20 | - [Is the bump on feedforward tuner normal?](./roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.md) 21 | - [Manual feedforward tuner overshoots](./roadrunner_056/manual_feedforward_tuner_overshooots.md) 22 | - [Manual Feedforward tuner opposite velocity](./roadrunner_056/manual_feed_forward_tuner_opposite_velocities.md) 23 | - [Robot localization makes circles when spinning in place](./roadrunner_056/robot_localization_makes_circle_when_spinning_in_place.md) 24 | - [Robot velocity plateaus below target velocity plateau](./roadrunner_056/robot_velocity_plateaus_below_target_velocity_plateau.md) 25 | - [Robot drifts to one side during feedforward tuning](./roadrunner_056/robot_drifts_to_one_side_during_manual_feedforward_tuning.md) 26 | - [Robot drifts while tuning follower PID](./roadrunner_056/robot_drifts_while_tuning_follower_pid.md) 27 | - [Robot drives full speed on start when following trajectory](./roadrunner_056/robot_drives_full_speed_on_start_when_following_trajectory.md) 28 | - [How to integrate a PIDF controller with Road Runner](./roadrunner_056/how_to_integrate_a_PIDF_controller_with_roadrunner/how_to_integrate_a_PIDF_controller_with_roadrunner.md) 29 | 30 | # Common Driver Station Error Messages 31 | - [`NullPointerException` on initialization](./common_ds_errors/npe_at_init/npe_at_init.md) 32 | 33 | # PID(F) Controllers 34 | - [Integrating a Custom PID(F) Controller](./pidf_controllers/integrating_a_custom_PIDF_controller.md) 35 | - [Syncing two linear slide motors using a PID(F) Controller](./pidf_controllers/syncing_two_linear_slide_motors_using_a_pidf_controller/syncing_two_linear_slide_motors_using_a_pidf_controller.md) 36 | 37 | # Electrical 38 | - [Why to only use USB 3.0](./electrical/why_we_should_only_use_usb_30.md) 39 | - [How to wire odometry pods](./electrical/how_to_wire_odometry_pods.md) 40 | 41 | # Miscellaneous 42 | - [Why Kotlin?](./misc/why_kotlin/why_kotlin.md) 43 | - [Terminology and Acronyms](./misc/terminology_and_acronyms.md) 44 | - [Improving Loop Times](./improving_loop_times/improving_loop_times.md) 45 | - [Pedro Pathing vs Road Runner](./misc/pedro_vs_roadrunner.md) 46 | 47 | --- 48 | [Contributors](./contributors.md) 49 | -------------------------------------------------------------------------------- /src/tags.md: -------------------------------------------------------------------------------- 1 | # Tags 2 | Cookbook includes a set of quick aliases to many of the articles. 3 | These can be easily accessed using tools like Carl custom tags. 4 | One is available here: https://carl.gg/t/2167631 5 | 6 | Here is a list of all of them: 7 | 8 | !cb tags -> this page 9 | 10 | !cb help -> this page 11 | 12 | !cb as -> https://cookbook.dairy.foundation/intro_to_programming/setup.html 13 | 14 | !cb git -> https://cookbook.dairy.foundation/intro_to_programming/intro_to_git.html 15 | 16 | !cb jdk -> https://cookbook.dairy.foundation/gradle/downgrading_gradle_jdk/downgrading_gradle_jdk.html 17 | 18 | !cb downgrade -> https://cookbook.dairy.foundation/gradle/downgrading_gradle_jdk/downgrading_gradle_jdk.html 19 | 20 | !cb gradle -> https://cookbook.dairy.foundation/gradle/dont_upgrade/dont_upgrade.html 21 | 22 | !cb emptylist -> https://cookbook.dairy.foundation/roadrunner_10/null_list_error_in_rr_10.html 23 | 24 | !cb builder -> https://cookbook.dairy.foundation/roadrunner_10/complete_trajectorybuilder_reference.html 25 | 26 | !cb bump -> https://cookbook.dairy.foundation/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.html 27 | 28 | !cb ffbump -> https://cookbook.dairy.foundation/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.html 29 | 30 | !cb overshoot -> https://cookbook.dairy.foundation/roadrunner_056/is_the_bump_on_manual_feedforward_tuner_normal.html 31 | 32 | !cb null -> https://cookbook.dairy.foundation/common_ds_errors/npe_at_init/npe_at_init.html 33 | 34 | !cb npe -> https://cookbook.dairy.foundation/common_ds_errors/npe_at_init/npe_at_init.html 35 | 36 | !cb 27971 -> https://cookbook.dairy.foundation/common_ds_errors/npe_at_init/npe_at_init.html 37 | 38 | !cb pid -> https://cookbook.dairy.foundation/pidf_controllers/integrating_a_custom_PIDF_controller.html 39 | 40 | !cb pidf -> https://cookbook.dairy.foundation/pidf_controllers/integrating_a_custom_PIDF_controller.html 41 | 42 | !cb pidsync -> https://cookbook.dairy.foundation/pidf_controllers/syncing_two_linear_slide_motors_using_a_pidf_controller/syncing_two_linear_slide_motors_using_a_pidf_controller.html 43 | 44 | !cb usb -> https://cookbook.dairy.foundation/electrical/why_we_should_only_use_usb_30.html 45 | 46 | !cb usb3 -> https://cookbook.dairy.foundation/electrical/why_we_should_only_use_usb_30.html 47 | 48 | !cb odo -> https://cookbook.dairy.foundation/electrical/how_to_wire_odometry_pods.html 49 | 50 | !cb kotlin -> https://cookbook.dairy.foundation/misc/why_kotlin/why_kotlin.html 51 | 52 | !cb loop -> https://cookbook.dairy.foundation/improving_loop_times/improving_loop_times.html 53 | 54 | !cb looptimes -> https://cookbook.dairy.foundation/improving_loop_times/improving_loop_times.html 55 | 56 | !cb pedrovsrr -> https://cookbook.dairy.foundation/misc/pedro_vs_roadrunner.html 57 | 58 | !cb rrvspedro -> https://cookbook.dairy.foundation/misc/pedro_vs_roadrunner.html 59 | 60 | !cb pedro -> https://cookbook.dairy.foundation/misc/pedro_vs_roadrunner.html 61 | 62 | !cb rrvpedro -> https://cookbook.dairy.foundation/misc/pedro_vs_roadrunner.html 63 | 64 | !cb tab -> https://cookbook.dairy.foundation/roadrunner_10/complete_trajectorybuilder_reference.html 65 | 66 | !cb trajectory -> https://cookbook.dairy.foundation/roadrunner_10/complete_trajectorybuilder_reference.html -------------------------------------------------------------------------------- /src/roadrunner_056/how_to_integrate_a_PIDF_controller_with_roadrunner/RoadRunnerPIDF.java: -------------------------------------------------------------------------------- 1 | public class RoadRunnerPIDF extends LinearOpMode { 2 | // the capitalization and snake_case is just convention because the values of an enum are constants 3 | public enum STATES { 4 | INIT, 5 | DRIVE_FORWARD, 6 | STRAFE_LEFT_AND_LIFT_SLIDES, 7 | DRIVE_BACKWARD, 8 | STOP; 9 | } 10 | 11 | private STATES previousState = STATES.INIT; 12 | private STATES currentState = STATES.INIT; 13 | private int targetPosition = 0; 14 | 15 | private TrajectorySequence forward; 16 | private TrajectorySequence strafeLeft; 17 | private TrajectorySequence backward; 18 | 19 | private SampleMecanumDrive drive; 20 | private DcMotorEx linearSlides; 21 | private PIDFController PIDF; 22 | 23 | public void runOpMode() { 24 | /* for the purpose of this recipe, I will be using linear slides with PIDF control to demonstrate. 25 | The linear slides will simply be called linearSlides. 26 | */ 27 | 28 | // linear slide initialization code 29 | // pidf initialization code 30 | 31 | drive = new SampleMecanumDrive(hardwareMap); 32 | drive.setPoseEstimate(new Pose2d()); 33 | 34 | forward = drive.TrajectorySequenceBuilder(new Pose2d()) 35 | .forward(10) 36 | .build(); 37 | 38 | strafeLeft = drive.TrajectorySequenceBuilder(forward.end()) 39 | .addDisplacementMarker(() -> { 40 | targetPosition = 800; 41 | }) 42 | .strafeLeft(10) 43 | .build(); 44 | 45 | backward = drive.TrajectorySequenceBuilder(strafeLeft.end()) 46 | .back(10) 47 | .build(); 48 | 49 | waitForStart(); 50 | 51 | currentState = STATES.DRIVE_FORWARD; 52 | 53 | while(opModeIsActive) { 54 | switch (currentState) { 55 | case (INIT): 56 | break; 57 | case (DRIVE_FORWARD): 58 | if (previousState != currentState) { 59 | // everything in here will run once when the state switches 60 | 61 | drive.followTrajectoryAsync(forward) 62 | previousState = STATES.DRIVE_FORWARD; 63 | } else if (!drive.isBusy()) { 64 | currentState = STATES.STRAFE_LEFT_AND_LIFT_SLIDES; 65 | } 66 | break; 67 | case (STRAFE_LEFT_AND_LIFT_SLIDES): 68 | if (previousState != currentState) { 69 | // inside this trajectory sequence the targetPosition is set and the slides will start updating 70 | drive.followTrajectorySequenceAsync(strafeLeft); 71 | } else if (!drive.isBusy() && linearSlides.atTarget()) { 72 | currentState = STATES.DRIVE_BACKWARD; 73 | } 74 | break; 75 | case (DRIVE_BACKWARD): 76 | if (previousState != currentState) { 77 | drive.followTrajectorySequenceAsync(backward); 78 | previousState = STATES.DRIVE_BACKWARD; 79 | } else if (!drive.isBusy()) { 80 | currentState = STATES.STOP; 81 | } 82 | break; 83 | case (STOP): 84 | break; 85 | } 86 | 87 | // outside of the switch we update our slides, that way they are always receiving new information 88 | drive.update(); 89 | double power = PIDF.calculate(linearSlides.getCurrentPosition(), targetPosition) 90 | linearSlides.setPower(power); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/improving_loop_times/improving_loop_times.md: -------------------------------------------------------------------------------- 1 | # Improving Loop Times 2 | 3 | ## Recipe 4 | 5 | **This recipe will cover various methods to improve loop times. At the end there are full examples of code that combine these methods** 6 | 7 | ### Why are fast loop times important? 8 | 9 | The more often your teleop is updated, the more responsive it will be to button clicks and joystick changes. 10 | This can make driving easier. 11 | Additionally, if you are using PID(F) controllers, the more frequently they update the more accurate they are and the less they oscillate. 12 | 13 | ### What causes slow loop times? 14 | 15 | Surprisingly, the main cause of slow loop times is not processing difficulties or code complexity. 16 | Most of the processing time is spent on communicating with hardware devices, known as hardware "reads" and hardware "writes". 17 | 18 | Hardware reads are when you are receiving data. 19 | For example, getting an encoder position, reading a color sensor, or accessing the IMU are all hardware reads. 20 | On the other hand, hardware writes are when you are sending data. 21 | For example, setting a motor power, setting a servo position, or configuring an LED are all hardware writes. 22 | 23 | ### Checking loop times 24 | 25 | Here is some basic code to measure your loop times in milliseconds. 26 | The more milliseconds your loop takes, the slower your loop times are. 27 | 28 | This measures looptimes over a single loop, which can lead to unstable readings, 29 | but is good enough. 30 | 31 | ```java 32 | {{#rustdoc_include MeasuringLoopTimes.java:6:}} 33 | ``` 34 | 35 | You may want to try: 36 | - Average looptimes over the whole runtime (totalRuntime / numberOfLoops). 37 | - Measuring looptimes over a window (over 100 loops, over 50 loops, etc). 38 | 39 | In order to get a better idea of your stable loop times. 40 | 41 | Some of the optimisations we look at may increase the looptime variance, as some 42 | loops may be very short, while others may be much longer. 43 | 44 | ### Bulk Reading 45 | 46 | Other than I2C devices, reading can be done all at once in a "bulk read" for a huge loop time improvement. 47 | 48 | By default, every time you do a hardware read, a new command is sent to retrieve it. 49 | Using one command to retrieve ALL the data is bulk reading. 50 | 51 | This recipe will not go into the different bulk reading modes. To learn more look [here](https://gm0.org/en/latest/docs/software/tutorials/bulk-reads.html). 52 | 53 | ### Caching Motor Powers 54 | 55 | So now let's try and reduce unnecessary hardware writes. 56 | 57 | If a motor is going at 0.5 power, and we keep setting the power to 0.5, the output of the motor will not change. 58 | However, each one of those `setPower()` commands is a hardware write which will delay your loop. 59 | A simple solution to this is to only send a new motor power when it is different from the previous. 60 | The SDK has this built in for this 'exactly equal' case. 61 | 62 | However, we can go even further then just difference to remove much more unnecessary writes. 63 | If the motor is currently running at 0.5 power, and you tell it to run at 0.51 power instead, it will have very little effect. 64 | However, it will unnecessarily perform a loop-delaying hardware write. 65 | 66 | Instead, you can store the last power sent to a motor and check every new `setPower()` command to only run if the new power is sufficiently different from the previous power. 67 | 68 | Implementations of hardware devices with these optimisations applied are 69 | available [here](https://github.com/Dairy-Foundation/CachingHardware). 70 | This github page provides the installation and usage instructions, so this 71 | recipe won't cover them. 72 | 73 | ### Full Examples 74 | 75 | This example uses [CachingHardware](https://github.com/Dairy-Foundation/CachingHardware) 76 | ```java 77 | {{#rustdoc_include CachingOptimizedOpMode.java:12:}} 78 | ``` 79 | -------------------------------------------------------------------------------- /src/roadrunner_10/BuilderReference.java: -------------------------------------------------------------------------------- 1 | // Robot waits for the specified time in seconds (NOT MILLISECONDS!) 2 | // This is a simple wait segment that is useful for running actions in between trajectories. 3 | 4 | .waitSeconds(5) 5 | 6 | 7 | 8 | // Robot turns counterclockwise by the specified angle 9 | // This turn is in radians, so you must convert your degrees to radians using `Math.toRadians()`. 10 | // If you see `Math.PI`, it is already in radians, and does not need `Math.toRadians()`. Degrees from 0 to 360 need to be converted to radians. 11 | // To turn clockwise, use a negative angle. 12 | 13 | .turn(-Math.PI / 6) // Turns clockwise by `Math.PI / 6` radians (30 degrees), ending at a heading of 0 degrees 14 | .turn(Math.PI / 6) // Turns counterclockwise by `Math.PI / 6` radians (30 degrees), ending at the original heading 15 | 16 | 17 | 18 | // Robot turns counterclockwise to the specified angle 19 | // This turn is in radians, so you must convert your degrees to radians using `Math.toRadians()`. 20 | // By default, the robot will turn in the shortest direction to the specified heading. 21 | // To turn in the opposite direction, you can add or subtract a very small number (1e-6) to the heading you want to turn to. 22 | // If it still does not work, you can use the `turn()` method instead. 23 | 24 | .turnTo(Math.toRadians(90)) // Turns to a heading of 90 degrees 25 | .turnTo(Math.PI / 6) // Turns to a heading of `Math.PI / 6` radians, ending at the original heading 26 | 27 | 28 | 29 | // `setTangent()` allows you to set a heading tangent on a trajectory, allowing you to follow a trajectory at arbitrary heading tangents 30 | // This is equivalent to specifying a custom tangent in the `TrajectoryBuilder()` constructor. 31 | 32 | .setTangent(Math.toRadians(90)) // Sets tangent to 90 degrees 33 | 34 | 35 | 36 | // If you see these hooks on the start and/or end of spline trajectories, you can use `setReversed()` to fix them 37 | // These hooks make your robot move backwards instead of forward or vice versa in splines, creating suboptimal paths. 38 | // This can be fixed by reversing the path using `setReversed(true)`. 39 | 40 | .setReversed(false) // Unreversed trajectory has hooks on the start and end 41 | .splineTo(new Vector2d(-48.0, -24.0), -Math.PI / 2) 42 | .setReversed(false) 43 | .splineTo(new Vector2d(-48.0, 0.0), Math.PI) 44 | 45 | 46 | 47 | .setReversed(true) // Reversed trajectory has no hooks on the start and end, and is smooth 48 | .splineTo(new Vector2d(-48.0, -24.0), -Math.PI / 2) 49 | .setReversed(false) 50 | .splineTo(new Vector2d(-48.0, 0.0), Math.PI) 51 | 52 | 53 | 54 | // By default, each trajectory is set to `setReversed(false)`, which does not reverse the paths. 55 | // This means that: 56 | .setReversed(false) 57 | .splineTo(new Vector2d(-48.0, -24.0), -Math.PI / 2) 58 | .setReversed(false) 59 | .splineTo(new Vector2d(-48.0, 0.0), Math.PI) 60 | 61 | // Is the same as: 62 | .splineTo(new Vector2d(-48.0, -24.0), -Math.PI / 2) 63 | .splineTo(new Vector2d(-48.0, 0.0), Math.PI) 64 | 65 | 66 | 67 | // Robot moves to the specified coordinates while maintaining its heading. 68 | // Both `strafeTo()` and `strafeToConstantHeading()` are equivalent. 69 | // So, if you start at a 90 degree angle, it will keep that angle the entire path. 70 | 71 | .strafeTo(new Vector2d(48, -48)) 72 | .strafeToConstantHeading(new Vector2d(48, -48)) 73 | 74 | 75 | 76 | // Robot moves to the specified coordinates while linearly interpolating between the start heading and a specified end heading 77 | // In other words, it constantly turns to a certain heading (once more, in radians) while moving to the specified coordinates. 78 | 79 | .strafeToLinearHeading(new Vector2d(48, -48), Math.toRadians(180)) 80 | 81 | 82 | 83 | // Robot moves to the specified coordinates while splinely interpolating between the start heading and a specified end heading 84 | // In other words, it constantly turns to a certain heading (once more, in radians) while moving to the specified coordinates. 85 | 86 | .strafeToSplineHeading(new Vector2d(48, -48), Math.toRadians(180)) 87 | 88 | 89 | 90 | // Robot moves to the specified x coordinate in the direction of the robot heading (straight line). 91 | // Both `lineToX()` and `lineToXConstantHeading()` are equivalent. 92 | // 🚨 Will cause an error if your heading is perpendicular to direction your robot is traveling! 🚨 93 | 94 | .lineToX(48) 95 | .lineToXConstantHeading(48) 96 | 97 | 98 | 99 | // Robot moves to the specified y coordinate in the direction of the robot heading (straight line). 100 | // Both `lineToY()` and `lineToYConstantHeading()` are equivalent. 101 | // 🚨 Will cause an error if your heading is perpendicular to direction your robot is traveling! 🚨 102 | 103 | .lineToY(36) 104 | .lineToYConstantHeading(36) 105 | 106 | 107 | 108 | // Robot moves to the specified coordinates in a spline path while following a tangent heading interpolator 109 | 110 | .splineTo(new Vector2d(48, 48), Math.PI / 2) 111 | 112 | 113 | 114 | // Robot moves to the specified coordinates in a spline path while following a tangent heading interpolator 115 | 116 | .setTangent(0) 117 | .splineTo(new Vector2d(48, 48), Math.PI / 2) 118 | 119 | 120 | 121 | // Robot moves to the specified coordinates in a spline path while keeping the heading constant 122 | // The robot maintains the heading it starts at throughout the trajectory. 123 | // To change the shape of the spline, change `endTangent`. 124 | 125 | .setTangent(0) 126 | .splineToConstantHeading(new Vector2d(48, 48), Math.PI / 2) 127 | 128 | 129 | 130 | // Robot moves to the specified coordinates in a spline path while separately linearly interpolating the heading 131 | // To change the shape of the spline, change `endTangent`. 132 | 133 | .setTangent(0) 134 | .splineToLinearHeading(new Pose2d(48, 48, 0), Math.PI / 2) 135 | 136 | 137 | 138 | // Robot moves to the specified coordinates in a spline path while separately spline interpolating the heading 139 | // To change the shape of the spline, change `endTangent`. 140 | 141 | .setTangent(0) 142 | .splineToSplineHeading(new Pose2d(48, 48, 0), Math.PI / 2) 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/roadrunner_056/how_to_integrate_a_PIDF_controller_with_roadrunner/how_to_integrate_a_PIDF_controller_with_roadrunner.md: -------------------------------------------------------------------------------- 1 | # How to Integrate a PIDF Controller With Roadrunner 2 | 3 | *This recipe will assume you have a functioning PIDF controller that has already been tuned. If you do not, refer to [integrating a custom PIDF controller](./how_to_integrate_a_PIDF_controller_with_roadrunner.md).* 4 | 5 | ## Ingredients 6 | 7 | 1. A PID or PIDF controller class 8 | 2. Tuned PID(F) gains 9 | 3. An OpMode or LinearOpMode 10 | 4. A Finite State Machine 11 | 12 | ## The Recipe 13 | 14 | ### PID(F) Controller and gains 15 | 16 | This recipe assumes you have 1) a PID(F) class that works and 2) tuned PID(F) gains. 17 | This recipe will not go over how to implement these; you should reference [integrating a custom PIDF controller](./how_to_integrate_a_PIDF_controller_with_roadrunner.md). 18 | 19 | ### Finite State Machines 20 | 21 | In short, a finite state machine is a code structure which allows code to run linearly while also having quasi-parallel actions running. 22 | The example we will be working with today is driving with Roadrunner while controlling linear slides. 23 | For a more indepth understanding of what finite state machines are, visit [gm0](https://gm0.org/en/latest/docs/software/concepts/finite-state-machines.html?highlight=finite). 24 | 25 | You can work with Finite State Machines in either a LinearOpMode or an OpMode, either work. 26 | For this recipe, we will be using a LinearOpMode. 27 | To use an OpMode, move everything before the while loop into the `init()` function and everything in the while loop into the `loop()` function. 28 | 29 | We will first have a full example and then break it down piece by piece. 30 | 31 | *This example is more like pseudocode than real code and is meant to demonstrate a methodology.* 32 | 33 | ```java 34 | {{#rustdoc_include RoadRunnerPIDF.java::}} 35 | ``` 36 | 37 | Okay, let's break this down piece by piece. 38 | First, what is an "enum" and why do we use them? 39 | Enums are a way to define a set of named constant values. 40 | They provide a convenient and readable way to work with predefined, named values in your code. 41 | Here, we used an enum to describe the various states the robot could be in. 42 | 43 | ```java 44 | {{#rustdoc_include RoadRunnerPIDF.java:3:9}} 45 | ``` 46 | 47 | By using names with meaning like these, it is much clearer when writing and reading the code what each block does. 48 | It also means we don't have to remember that state 0 means START and state 1 means DRIVE_FORWARD, etc. 49 | 50 | Next, we initialize everything and build our trajectories. 51 | The important one to note is creating `strafeLeft`, which includes slide movement. 52 | 53 | ```java 54 | {{#rustdoc_include RoadRunnerPIDF.java:38:43}} 55 | ``` 56 | 57 | We used a displacement marker, which tells Roadrunner to run this code at the specified position along the trajectory. 58 | The `() -> {}` is the lambda format for a one time use function. 59 | The empty parentheses indicate that the function requires no arguments, and the curly braces denote the start of the function. 60 | In this case, we're just setting the `targetPosition` variable, but this marker could include setting servo position, reading sensors, or anything else really. 61 | 62 | ```java 63 | {{#rustdoc_include RoadRunnerPIDF.java:57:66}} 64 | ``` 65 | 66 | So now we're getting to the Finite State Machine (FSM) part. 67 | The first part of this case, which you'll see in each part, is checking whether previous and current states are equal. 68 | This allows us to run code the first time it enters this state, like starting a trajectory (in this example). 69 | Then inside that same block, we also need to set the previous state to the one we're in. 70 | 71 | The `else if` just checks if we're done with this state to detect when to move on. 72 | This is the transition trigger. 73 | In this case, it detects when the Roadrunner trajectory finishes. 74 | 75 | The next case is the more interesting one. 76 | 77 | ```java 78 | {{#rustdoc_include RoadRunnerPIDF.java:67:74}} 79 | ``` 80 | 81 | Here we have the same structure. 82 | However, this time our transition trigger is finishing the Roadrunner trajectory and the linear slides reaching their target. 83 | Because this runs in a loop, once the displacement marker triggers and changes the targetPosition, the PID update that runs at the end of every loop will move the linear slides accordingly. 84 | 85 | It is also important to note that when using async following, you must call drive.update() once every loop. 86 | This allows Roadrunner to track the robot's movement and to ensure the motors are following the trajectory. 87 | Without it, the robot will not move. 88 | 89 | Whew! You should now be able to integrate a PID(F) controller with Roadrunner trajectories. 90 | 91 | This example was meant to be general and explain the structure and concepts needed to make PID(F) controllers work with Roadrunner. 92 | It will almost certainly require changes to make it work exactly how you wish, so don't worry if your code doesn't look exactly like this example! 93 | 94 | ### State Factory 95 | 96 | [State Factory](https://state-factory.gitbook.io/state-factory/installation) is a library which helps abstract a lot of the code of a finite state machine. 97 | It also helps ensure you don't forget to write a break or an exit case. 98 | 99 | *This recipe will not cover the installation of State Factory. 100 | Please follow the instructions on their gitbook to install it.* 101 | 102 | So, we're going to write the same finite state machine but this time using State Factory. 103 | 104 | ```java 105 | {{#rustdoc_include RoadRunnerPIDFSF.java::}} 106 | ``` 107 | 108 | These two examples both do the exact same thing. 109 | This introduction to State Factory was mostly meant to show how it can simplify writing FSMs. 110 | 111 | Android studio may recommend changing something like `() -> !drive.isBusy()` to `!drive::isBusy`. 112 | These are simply two different ways to write the same thing. 113 | The double colon works like `class/instance::method`. 114 | 115 | **It is important to note that these were extremely simple FSMs and do not demonstrate their full capabilities. 116 | This was simply meant to show you a way to integrate RoadRunner and a PIDF controller.** 117 | 118 | *Last Updated: 2024-01-23* -------------------------------------------------------------------------------- /src/intro_to_programming/setup.md: -------------------------------------------------------------------------------- 1 | # Development Environment Setup 2 | This is a guide designed to assist new FTC programmers setup the [Android Studio](https://developer.android.com/studio/) environment to program a robot. 3 | ## Ingredients 4 | A **Computer** that has the [required specs](https://developer.android.com/studio/install) for [Android Studio](https://developer.android.com/studio/) and installing [Java](https://www.oracle.com/java/technologies/downloads/).
5 | Access to **Admin Permissions** on aforementioned computer.
6 | 7 | ## Software to be Installed 8 | **FTC SDK**: [FtcRobotController](https://github.com/FIRST-Tech-Challenge/FtcRobotController)
9 | **IDE**: [Android Studio](https://developer.android.com/studio/)
10 | **Java**: Any recent version of [Java](https://www.oracle.com/java/technologies/downloads/)
11 | **ADB**: [Android Debug Bridge](https://developer.android.com/tools/releases/platform-tools)
12 | 13 | ## Recipe (Installation) 14 | 1. **Download and Install [Java](https://www.oracle.com/java/technologies/downloads/)**: 15 | **
NOTE: Installing [Java](https://www.oracle.com/java/technologies/downloads/) may require Admin Permissions.** 16 | - Download the latest version of [Java](https://www.oracle.com/java/technologies/downloads/). 17 | - Run the installer and follow the on-screen instructions.
18 |
19 | 2. **Download and Install [Android Studio](https://developer.android.com/studio/)**: 20 | **
NOTE: Installing [Android Studio](https://developer.android.com/studio/) may require Admin Permissions.** 21 | - Download the latest version of [Android Studio](https://developer.android.com/studio/). 22 | - Run the installer and follow the on-screen instructions. 23 | - Open [Android Studio](https://developer.android.com/studio/).
24 |
25 | 3. **Download and Open [FtcRobotController](https://github.com/FIRST-Tech-Challenge/FtcRobotController)**: 26 | - In the [FtcRobotController](https://github.com/FIRST-Tech-Challenge/FtcRobotController) GitHub repository, press the blue **code** button and press [download zip](https://github.com/FIRST-Tech-Challenge/FtcRobotController/archive/refs/heads/master.zip). 27 | - You can alternatively use [Github Desktop](https://desktop.github.com/) to open [FtcRobotController](https://github.com/FIRST-Tech-Challenge/FtcRobotController) in [Android Studio](https://developer.android.com/studio/), which is not covered in this Recipe. 28 | - Extract the contents of the zip file to a folder (typically in your Downloads or Documents folder). 29 | - In [Android Studio](https://developer.android.com/studio/), press File → Open (⌘ + O on Mac or Win + O on Windows). 30 | - Select the folder you extracted the zip file to, and press open. **DO NOT** open any folder inside the extracted folder. 31 | - On MacOS, you can alternatively drag the folder from Finder onto the [Android Studio](https://developer.android.com/studio/) icon in your taskbar to open the folder. 32 | - Wait for the project to load. You should end up with 3 folders in the Android view panel (which you should automatically be moved to once ready): **FtcRobotController**, **TeamCode**, and **Gradle Scripts**.
33 |
34 | 4. **Install** [**ADB**](https://developer.android.com/studio/releases/platform-tools): 35 | **
NOTE: Installing [ADB](https://developer.android.com/studio/releases/platform-tools) may require Admin Permissions.** 36 | - Download the latest version of [ADB](https://developer.android.com/studio/releases/platform-tools). 37 | Press on one of the three links depending on your operating system. 38 | - Extract the contents of the zip file to a folder (typically in your Downloads or Documents folder). 39 | - Add the folder to your system's PATH variable: 40 | - **Windows**: 41 | - Open the start menu and search for "Environment Variables", and press enter. 42 | - Click on "Edit the system environment variables". 43 | - Click on "Environment Variables". 44 | - In the "System variables" section, find the "Path" variable and click "Edit". 45 | - Click "New" and paste the path to the folder where you extracted the zip file. 46 | - Click "OK" on all the windows. 47 | - **Mac**:
48 | - Option 1 - Using [Homebrew](https://brew.sh/) (Highly Recommended)
49 | [Homebrew](https://brew.sh/) is a package manager for Mac. This is the easiest way and will provide automatic updates. 50 | - Install the [Homebrew](https://brew.sh/) package manager by running the following command in a terminal: 51 | ```bash 52 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 53 | ``` 54 | 55 | - Then, run this command to install [ADB](https://developer.android.com/studio/releases/platform-tools) using [Homebrew](https://brew.sh/): 56 | ```bash 57 | brew install android-platform-tools 58 | ``` 59 | - Option 2 - Manual Installation 60 | - Go to your Downloads folder with this command in terminal: 61 | ```bash 62 | cd ~/Downloads/ 63 | ``` 64 | - Then, to avoid deleting the ADB files, move the downloaded file to a new folder (the commands below should still work with modern versions of MacOS): 65 | ```bash 66 | mkdir ~/.android-sdk-macosx 67 | mv platform-tools/ ~/.android-sdk-macosx/platform-tools 68 | ``` 69 | - Add `platform-tools` to your path 70 | ```bash 71 | echo 'export PATH=$PATH:~/.android-sdk-macosx/platform-tools/' >> ~/.bash_profile 72 | ``` 73 | - Reload your terminal profile (or restart your terminal): 74 | ```bash 75 | source ~/.bash_profile 76 | ``` 77 | - **Linux**: 78 |
[ADB](https://developer.android.com/studio/releases/platform-tools) should already be installed by default with the installation of Android Studio. If not, you can use the following steps below to install [ADB](https://developer.android.com/studio/releases/platform-tools) manually:
79 |
80 | - Open a terminal window and run the following command: 81 | ```bash 82 | nano ~/.bashrc 83 | ``` 84 | - Add the following line to the file: 85 | ```bash 86 | export PATH=$PATH:/path/to/adb/folder 87 | ``` 88 | - Press `Ctrl + X`, then `Y`, then `Enter` to save the file. 89 | - Run the following command: 90 | ```bash 91 | source ~/.bashrc 92 | ``` 93 | - Finally, to check that your properly installed [ADB](https://developer.android.com/studio/releases/platform-tools), in the Android Studio Terminal (Control + F12 on Windows or ⌘ + F12 on Mac), or on your default/preferred terminal, run the following command: 94 | ```bash 95 | adb devices 96 | ``` 97 | - If you see a message saying `List of devices attached`, you have successfully installed ADB (even if there are no devices attached). 98 | 99 | Congratulations! You have successfully installed the necessary software to program an FTC robot. You can now start programming your robot. 100 | 101 | ## Troubleshooting 102 | - If you have questions/issues with the installation process, the [Unofficial FTC Discord](https://discord.gg/first-tech-challenge) has many experienced programmers who can help you with all sorts of issues, including installation issues. 103 | -------------------------------------------------------------------------------- /src/misc/why_kotlin/why_kotlin.md: -------------------------------------------------------------------------------- 1 | # Why do some FTC programmers use Kotlin? Should I switch? 2 | 3 | *Have you ever seen Kotlin mentioned in the context of FTC code?* 4 | 5 | *Are you curious about why some FTC programmers like to use Kotlin for their code bases?* 6 | 7 | Kotlin is a language with very high cross-compatability with Java, which means it can be used to write your FTC code. 8 | 9 | FIRST provides official instructions 10 | for adding Kotlin to your project [here.](https://ftc-docs.firstinspires.org/en/latest/programming_resources/shared/installing_kotlin/Installing-Kotlin.html) 11 | 12 | ## Ingredients 13 | 14 | 1. Good understanding of Java 15 | 2. Interest in learning and exploring Kotlin 16 | 17 | ## The Recipe 18 | 19 | Kotlin is a language that makes a very solid attempt at modernizing Java. 20 | It makes writing common Java patterns concise. 21 | Kotlin also makes it easy to write safer code that is less likely to have strange bugs or throw confusing NullPointerExceptions. 22 | 23 | Kotlin is unlikely to be particularly useful to you if you are not using Object-Oriented aspects of Java already. 24 | If you are just writing \[Linear\]OpModes but are not writing your own classes, Kotlin is probably not for you. 25 | While Kotlin certainly does offer some nice features in this environment, the challenges that come with using Kotlin may also prove hard to overcome unless you are writing more complex and involved code. 26 | It is also advisable not to try to switch to Kotlin at the same time as learning more Object-Oriented skills. 27 | 28 | Due to Kotlin's concise nature, it can sometimes prove difficult to read. 29 | Java likes to put everything out in the open and be very direct and specific, while Kotlin tends to imply much more. 30 | 31 | This recipe will cover some basics of Kotlin syntax with direct comparisons to Java. 32 | 33 | ### Vars and Vals 34 | 35 | A big part of Kotlin is its changes to fields, getters, setters, and how they interact with parameters from constructors. 36 | 37 | The following two snippets are effectively equivalent: 38 | 39 | ```kt 40 | {{#rustdoc_include VarsAndVals.kt:2:6}} 41 | ``` 42 | 43 | ```java 44 | {{#rustdoc_include VarsAndVals.java:2:30}} 45 | ``` 46 | It's pretty clear that Kotlin saves a lot of work on the front of writing getters and setters. 47 | While this isn't too big of a deal, Kotlin makes itself invaluable in enforcing the usage of these functions in a syntactically shorter manner. 48 | 49 | ```kt 50 | {{#rustdoc_include VarsAndVals.kt:8:19}} 51 | ``` 52 | 53 | ```java 54 | {{#rustdoc_include VarsAndVals.java:32:43}} 55 | ``` 56 | 57 | Kotlin enforces the use of getters and setters for property access, but uses the property access syntax! 58 | 59 | Already, our Kotlin code is ~2.5 times shorter than Java. 60 | 61 | If you're worried about defining custom getters and setters, Kotlin allows that too. 62 | More detail is available in the [Kotlin docs](https://kotlinlang.org/docs/properties.html#getters-and-setters). 63 | Kotlin allows for a fairly wide range of cool features around this concept. 64 | 65 | ### Storing Constructor Parameters 66 | 67 | Kotlin makes it super easy to take in constructor parameters and store them in the class. 68 | The following two snippets are also equivalent: 69 | 70 | ```kt 71 | {{#rustdoc_include ConstructorParams.kt}} 72 | ``` 73 | 74 | ```java 75 | {{#rustdoc_include ConstructorParams.java}} 76 | ``` 77 | 78 | In this case, what was numerous lines in Java is only one in Kotlin. 79 | The Kotlin version is even a little easier to read! 80 | 81 | ### Default Values In Methods and Constructors 82 | 83 | Kotlin makes it easy to specify default values to functions and constructors. 84 | The following two snippets are equivalent: 85 | 86 | ```kt 87 | {{#rustdoc_include DefaultValues.kt}} 88 | ``` 89 | 90 | ```java 91 | {{#rustdoc_include DefaultValues.java}} 92 | ``` 93 | 94 | Kotlin makes this a little more powerful than demonstrated here, but for most situations, this is pretty straightforward. 95 | 96 | ### Null Safety 97 | 98 | Kotlin makes it easy to work with values that can and can't be null, much easier than Java: 99 | 100 | ```kt 101 | val neverNull: Int = 10 102 | val nullable: Int? = null 103 | ``` 104 | The `?` lets us know that the variable could be null, and Kotlin will throw compilation warnings if we try to use it without checking and handling it. 105 | 106 | Some more examples: 107 | 108 | ```kt 109 | fun addOrThrow(a: Int?, b: Int?) { 110 | val safeA = a ?: throw IllegalStateException("a is null") 111 | val safeB = b!! 112 | return safeA + safeB 113 | } 114 | ``` 115 | 116 | The `?:` operator is known as the Elvis operator (after it's resemblance to Elvis Presley) and means that the code after it gets run only if the left-hand side is null. 117 | This allows the function to immediately exit and throw an error before any further processing occurs. 118 | 119 | The `!!` operator is a shortcut for this operation. 120 | The Elvis operator is more powerful and flexible, but if you don't want to throw a specific error and instead crash immediately, the `!!` operator will enforce that the value isn't null. 121 | 122 | Finally: 123 | 124 | ```kt 125 | fun nullSafeMethodCall(a: Int?): Double { 126 | return a?.toDouble() ?: throw IllegalStateException("a is null") 127 | } 128 | ``` 129 | 130 | We combine the concept above with the `?.` operator, which performs a null safe method call. 131 | If `a` is null, Kotlin won't try to call `.toDouble()` on it and will just return null, 132 | which will then be caught by the Elvis operator! 133 | 134 | ### Accessing Hardware 135 | Because of Kotlin's null safety system, accessing hardware must be done differently. 136 | There are a few ways to do this, but the one we'll show here is to use `by lazy`: 137 | ```kt 138 | // by lazy will only initialize this variable the first time it is used. 139 | // This prevents it from ever being null, but also allows you to initialize it only after your opmode begins. 140 | val arm by lazy { hardwareMap["arm"] as DcMotorEx } // Alternately hardwareMap.get(DcMotorEx::class.java, "arm") also works here 141 | 142 | override fun init() { // or runOpMode() for LinearOpModes 143 | // Since we used by lazy, accessing the arm in any way will automatically initialize it: 144 | arm.power = 1.0 145 | telemetry.addData("armPos", arm.currentPosition) 146 | telemetry.update() 147 | // Note that, since we used by lazy, we do NOT need to put !! after arm. 148 | } 149 | ``` 150 | There are other options to do this as well such as the `lateinit` keyword. 151 | 152 | ### Overview 153 | 154 | Kotlin has a lot more to it than this short overview, but these are some of the features that make a big common difference with Java. 155 | Hopefully, this arms you with an expectation of what the rest of Kotlin is like, and some of the reasons that more advanced FTC programmers like to choose it over Java. 156 | 157 | [Kotlin official documentation](https://kotlinlang.org/docs/home.html) 158 | 159 | Overall, the best way to learn is just to jump in and give it a try. 160 | If you get stuck, search it up, or take a look at the [docs](https://kotlinlang.org/docs/home.html) again! 161 | 162 | If you feel like you need a complete example of using Kotlin for FTC, I advise you ask in the FTC Discord. 163 | Most people who use Kotlin write fairly complex codebases and use different combinations of libraries, so you might need to ask some questions to find an example relevant to you. 164 | 165 | Another good place to search for Kotlin code in an FTC context is in libraries. 166 | Many FTC software libraries such as [Roadrunner](https://github.com/acmerobotics/road-runner) are written in Kotlin, so they can provide great usage examples. 167 | 168 | 169 | *Last updated: 2024-05-29* 170 | -------------------------------------------------------------------------------- /src/misc/pedro_vs_roadrunner.md: -------------------------------------------------------------------------------- 1 | # Pedro Pathing vs Road Runner 2 | 3 | [**Pedro Pathing**](https://pedropathing.com) and 4 | [**RoadRunner**](https://rr.brott.dev) are two popular autonomous movement 5 | libraries for FTC. The goal of this article is to provide an unbiased 6 | comparison to help you decide which to use. The goal of this article is 7 | **not** to recommend one over the other. 8 | 9 |
10 | 11 | | Aspect | Pedro Pathing | RoadRunner | 12 | |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 13 | | Following Strategy | Uses three PID controllers: translational, heading, and drive, along with centripetal force correction. [^pids] | Generates and follows motion-profiled trajectories[^traj] by using a combination of feedforward and feedback control[^control]. | 14 | | Visualizer | Has a no-code, web-based [path generator and visualizer](https://visualizer.pedropathing.com) that can export code for paths. | Has a code-based [path visualizer](https://github.com/acmerobotics/meepmeep) that visualizes paths defined in code. | 15 | | Tuning | Half automatic and half manual. Has six automatic tuning steps and four manual tuning steps[^pedro-tuning], although the manual steps take slightly longer. | Mostly automatic. Has four automatic tuning steps and two manual tuning steps[^rr-tuning], although the manual steps take slightly longer. If using the SparkFun OTOS for localization there are four additional automatic tuning steps. | 16 | | Loop Time Optimizations | Automatically implements motor write caching with a configurable cache tolerance.[^caching] | Automatically implements bulk reading.[^bulk] | 17 | | Good At | Correction for unexpected disturbances.[^correction] | Time-consistent trajectory following. | 18 | | Coordinate System | Custom coordinate system. Provides a [`PoseConverter`](https://github.com/Pedro-Pathing/PedroPathing/blob/main/ftc/src/main/java/com/pedropathing/ftc/PoseConverter.java) for converting to and from the official FIRST coordinate system. | Uses the official FIRST coordinate system. | 19 | | Command System | Does not come with a built-in command system,
although can be integrated with others. [NextFTC](https://nextftc.dev) and [SolversLib](https://docs.seattlesolvers.com) both provide built-in integration with Pedro Pathing. Mercurial has [two sample repos](https://docs.dairy.foundation/Samples/user_samples) using Pedro Pathing. | Has a built-in actions system. Also has an example on the docs for usage with FTCLib. [NextFTC](https://nextftc.dev) provides built-in integration with RoadRunner. Mercurial has [two sample repos](https://docs.dairy.foundation/Samples/user_samples) using RoadRunner. | 20 | | Logging | Automatically logs many values to telemetry, but does not log to a file. Data such as current position can be logged by the user by using a third-party library such as [PsiKit](https://psilynx.github.io/PsiKit) for replay with [AdvantageScope](https://advantagescope.org). | Logs many values automatically to telemetry and to a file during every OpMode run.[^logs] Uses a custom log format that is supported by [AdvantageScope](https://advantagescope.org). | 21 | | Drivetrain Support | Has built-in support for mecanum drivetrains. Users can provide a custom implementation of the `Drivetrain` interface to use another drivetrain[^custom-drivetrain], but does not support nonholonomic drivetrains such as tank. | Has built-in support for mecanum and tank drivetrains. Does not support any other drivetrains.[^rr-drivetrains] | 22 | 23 |
24 | 25 | [^pids]: [Pedro Pathing docs](https://pedropathing.com/docs/pathing) 26 | [^traj]: [RoadRunner trajectories](https://rr.brott.dev/docs/v0-5/tour/trajectories/): 27 | Although these docs are for RoadRunner v0.5, the concepts are the same for v1.0. 28 | [^control]: [Feedback](https://rr.brott.dev/docs/v0-5/tour/pid-control/) and 29 | [feedforward](https://rr.brott.dev/docs/v0-5/tour/feedforward-control/) 30 | control docs. 31 | [^pedro-tuning]: [Pedro Pathing tuning docs](https://pedropathing.com/docs/pathing/tuning) 32 | [^rr-tuning]: [RoadRunner tuning docs](https://rr.brott.dev/docs/v1-0/tuning/) 33 | [^caching]: [Cache tolerance in Pedro constants](https://github.com/Pedro-Pathing/PedroPathing/blob/main/ftc/src/main/java/com/pedropathing/ftc/drivetrains/MecanumConstants.java#L32) 34 | [^bulk]: [Bulk reading in `Encoder`](https://github.com/acmerobotics/road-runner-ftc/blob/master/RoadRunner/src/main/java/com/acmerobotics/roadrunner/ftc/Encoders.kt#L132) 35 | [^logs]: [RoadRunner log files](https://rr.brott.dev/docs/v1-0/log-files/) 36 | [^custom-drivetrain]: [Custom drivetrains](https://pedropathing.com/docs/pathing/custom/drivetrain) 37 | [^correction]: [Pedro Pathing homepage](https://pedropathing.com/) 38 | [^rr-drivetrains]: [RoadRunner drive classes](https://rr.brott.dev/docs/v1-0/tuning/#drive-classes) -------------------------------------------------------------------------------- /src/pidf_controllers/integrating_a_custom_PIDF_controller.md: -------------------------------------------------------------------------------- 1 | # Integrating a Custom PID(F) Controller 2 | 3 | *This recipe does not cover usage for Roadrunner or Command-Based structures.* 4 | 5 | [PID(F) controllers](https://www.ctrlaltftc.com/the-pid-controller) are some of the most used controllers in FTC. 6 | However, it can be confusing and challenging to properly integrate them into your OpModes. 7 | This recipe will go over an example of how to integrate a PID(F) controller alongside a manual control system. 8 | 9 | ## Ingredients 10 | 11 | 1. A PID or PIDF controller class (this should be a file that is something like PIDFController.java, or you may use a pre-made one from a library like FTCLib). 12 | 2. A use case for the PID(F). 13 | 3. An OpMode or LinearOpMode. 14 | 15 | ## The Recipe 16 | 17 | ### Creating a PID(F) Controller 18 | 19 | The first part of using a PID(F) controller is creating one. 20 | To do this, we need to declare the PID(F) controller within the OpMode: 21 | 22 | ```java 23 | package org.firstinspires.ftc.teamcode; 24 | 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; // This example is for a LinearOpMode, though a similar idea applies to regular OpModes. 27 | import org.firstinspires.ftc.teamcode.controllers.PIDController; // This may vary depending on what implementation you are using. 28 | import org.firstinspires.ftc.teamcode.controllers.PIDFController; // This may vary depending on what implementation you are using. 29 | 30 | @TeleOp 31 | public class ExampleOpMode extends LinearOpMode { 32 | 33 | // This line creates a PIDF controller named examplePIDF that has coefficients of: 34 | // kP = 0 35 | // kI = 0 36 | // kD = 0 37 | // kF = 0 38 | private PIDFController examplePIDF = new PIDFController(0, 0, 0, 0); 39 | 40 | // This line creates a PID controller named examplePID that has coefficients of: 41 | // kP = 0 42 | // kI = 0 43 | // kD = 0 44 | private PIDController examplePID = new PIDController(0, 0, 0); 45 | 46 | @Override 47 | public void runOpMode() { 48 | // OpMode code goes here 49 | } 50 | 51 | } 52 | ``` 53 | 54 | Now that we have our PID(F) controller, we need to use it! 55 | One of the most common use cases for a PID(F) controller is moving a motor to a certain motor encoder position. 56 | As an example, let's say we have a linear slide, and want to move it to 500 ticks when we press "a." 57 | We also want to be able to move it up and down using the triggers. 58 | The following code is for a LinearOpMode (the `while (opModeIsActive())` section would go in the `loop()` function for a OpMode): 59 | 60 | ```java 61 | 62 | 63 | public void runOpMode() { 64 | // Put all of your initialization here. 65 | DcMotor slides = hardwareMap.dcMotor.get("slides"); 66 | waitForStart(); 67 | 68 | int targetPosition = 500; 69 | 70 | // We will use this variable to determine if we want the PIDF to run. 71 | boolean usePIDF = false; 72 | 73 | Gamepad lastGamepad1 = new Gamepad(); 74 | Gamepad lastGamepad2 = new Gamepad(); 75 | 76 | while (opModeIsActive()) { 77 | 78 | // This is a rising-edge detector that runs if and only if "a" was pressed this loop. 79 | if (gamepad1.a && !lastGamepad1.a) { 80 | usePIDF = true; 81 | } 82 | 83 | 84 | if (gamepad1.left_trigger > 0) { 85 | slides.setPower(gamepad1.left_trigger); 86 | 87 | // If we get any sort of manual input, turn PIDF off. 88 | usePIDF = false; 89 | } else if (gamepad1.right_trigger > 0) { 90 | slides.setPower(gamepad1.right_trigger); 91 | 92 | // If we get any sort of manual input, turn PIDF off. 93 | usePIDF = false; 94 | } else if (usePIDF) { 95 | // Sets the slide motor power according to the PIDF output. 96 | slides.setPower(examplePIDF.calculate(slides.getCurrentPosition(), targetPosition)); 97 | } 98 | 99 | lastGamepad1 = gamepad1; 100 | lastGamepad2 = gamepad2; 101 | } 102 | } 103 | 104 | 105 | ``` 106 | 107 | This is a lot, so let's break it down piece by piece. 108 | First, we initialize our slide motor, which we call `slides`. 109 | 110 | ```java 111 | DcMotor slides = hardwareMap.dcMotor.get("slides"); 112 | ``` 113 | 114 | Next, we wait for the program to start and declare some variables. 115 | 116 | ```java 117 | waitForStart(); 118 | 119 | int targetPosition = 500; 120 | 121 | // We will use this variable to determine if we want the PIDF to run. 122 | boolean usePIDF = false; 123 | 124 | Gamepad lastGamepad1 = new Gamepad(); 125 | Gamepad lastGamepad2 = new Gamepad(); 126 | ``` 127 | 128 | `targetPosition` is simply the position we want the slides to go to, which is 500. 129 | `usePIDF` stores the state of our system, i.e. whether we want to run the PIDF or use manual control. 130 | `lastGamepad1` and `lastGamepad2` are used for [Rising Edge Detectors](https://gm0.org/en/latest/docs/software/tutorials/gamepad.html?detector#rising-edge-detector). 131 | In short, they detect when a button begins to be pressed, and ignore when it is held. 132 | 133 | The next part is the while loop, which ensures that the code runs in a loop until the OpMode stops. 134 | 135 | ```java 136 | while (opModeIsActive()) 137 | ``` 138 | 139 | We then use a [Rising Edge Detector](https://gm0.org/en/latest/docs/software/tutorials/gamepad.html?detector#rising-edge-detector) to check if "a" was just pressed. 140 | If it was, we set `usePIDF` to true to tell the program to move to the target position. 141 | 142 | ```java 143 | // This is a rising-edge detector that runs if and only if "a" was pressed this loop. 144 | if (gamepad1.a && !lastGamepad1.a) { 145 | usePIDF = true; 146 | } 147 | ``` 148 | 149 | The next part is a little complicated, but the idea is that we only want to call `slide.setPower()` once, so we group all the ways it can be called together so that they can't happen at the same time. 150 | 151 | First, we check if the left trigger is pressed. 152 | If it is, we set the slide power to an appropriate value and switch to manual control mode by setting `usePIDF` to `false`. 153 | 154 | ```java 155 | if (gamepad1.left_trigger > 0) { 156 | slides.setPower(gamepad1.left_trigger); 157 | 158 | // If we get any sort of manual input, turn PIDF off. 159 | usePIDF = false; 160 | } 161 | ``` 162 | 163 | Next, we do the same check, but for the right trigger. 164 | 165 | 166 | ```java 167 | else if (gamepad1.right_trigger > 0) { 168 | slides.setPower(gamepad1.right_trigger); 169 | 170 | // If we get any sort of manual input, turn PIDF off. 171 | usePIDF = false; 172 | } 173 | ``` 174 | 175 | Note that we use `else` to only run this code if the left trigger is not pressed. 176 | This prevents pressing both triggers at the same time from causing any issues. 177 | 178 | **Tip:** If your triggers return nonzero values even when they are not being pressed, you can increase the minimum value (the `0` in the statement `if (gamepad1.left_trigger > 0)`) from `0` to something like `0.1`. 179 | 180 | Finally, if there are no manual inputs, and we are in the PID(F) state, we run the PID(F). 181 | 182 | ```java 183 | else if (usePIDF) { 184 | // Sets the slide motor power according to the PIDF output. 185 | slides.setPower(examplePIDF.calculate(slides.getCurrentPosition(), targetPosition)); 186 | } 187 | ``` 188 | 189 | We also update our stored gamepads to allow the rising-edge detector to work. 190 | 191 | ```java 192 | lastGamepad1 = gamepad1; 193 | lastGamepad2 = gamepad2; 194 | ``` 195 | 196 | This is a pretty standard way of using the PID(F) output to set a motor power. 197 | `slides.getCurrentPosition()`, as the name implies, just returns the current slide position, in ticks. 198 | The [*FTCLib*](https://ftclib.org/) PID(F) assumes that the first input of the function is the place where your motor is, and the second input is the place where your motor wants to be. 199 | We will be using the *FTCLib* PID(F) syntax here for the sake of having some standard, but either way works. 200 | 201 | If you've read through this entire thing, then congrats! 202 | You should now have a fully functioning PID(F) controller that you can implement anywhere, even in conjunction with manual control. 203 | 204 | Note that the example we went through is just one way PID(F)s can be used, and there are many ways to achieve this result. 205 | Don't worry if your code doesn't look exactly like this example! 206 | 207 | As an aside, the technique we used to make sure the PID(F) control and manual control did not interfere is a simple version of what's known as a [**Finite State Machine**](https://gm0.org/en/latest/docs/software/concepts/finite-state-machines.html). 208 | This idea of having multiple possible states and only running one at a time to ensure they don't interfere can be used for much more complex systems, such as controlling an entire Autonomous! 209 | 210 | Best of luck with your code! 211 | -------------------------------------------------------------------------------- /src/intro_to_programming/intro_to_git.md: -------------------------------------------------------------------------------- 1 | # Introduction to Git and GitHub 2 | This guide is designed to help FTC teams understand how to use Git and GitHub to track changes, 3 | collaborate better, and recover previous versions. 4 | There are often multiple ways to achieve the same task when using Git, 5 | so this guide will explain the method we think is the easiest. 6 | 7 | This guide also assumes you are using the base FIRST Tech Challenge SDK. 8 | If you are instead using the RoadRunner quickstart or similar, use that in place of the SDK. 9 | 10 | ### Git vs GitHub 11 | **Git** is a version control system that tracks changes in code over time, allowing you to collaborate effectively. 12 | **GitHub** is a platform that hosts Git repositories. 13 | Though there are many Git hosting platforms, including GitLab and Bitbucket, 14 | this guide focuses on GitHub because it is the easiest to use with the FTC SDK. 15 | 16 | ## Ingredients 17 | 1. Internet access 18 | 2. A computer 19 | 3. Android Studio 20 | 4. A [GitHub](https://github.com/) account and organization. 21 | - To create an account, follow the steps on [GitHub](https://docs.github.com/en/get-started/start-your-journey/creating-an-account-on-github). 22 | - While creating an organization is optional, it is highly recommended for FTC teams. 23 | Follow the directions on [GitHub](https://docs.github.com/en/organizations/collaborating-with-groups-in-organizations/creating-a-new-organization-from-scratch). 24 | - Note that user accounts can only create one fork of a repository, while organization accounts can create multiple. 25 | This makes it easier for your team to set up a repository for each season. 26 | 27 | ## Recipe 28 | ### 0. Installing Git 29 | The easiest way to install Git on your device is 30 | to download it from [Git's download page](https://git-scm.com/downloads). 31 | Select your operating system and follow the instructions on the website. 32 | 33 | ### 1. Forking the Repository 34 | This step only needs to be done once each season. 35 | 36 | > A Fork on GitHub is a copy of another repository on GitHub from one account to another account. 37 | > The new forked repository retains a parent-child relationship with the origin repository. 38 | > Forks are typically used when software will have an independent line of development, 39 | > such as when FTC teams develop their own team code 40 | > using the FIRST-Tech-Challenge/FtcRobotController repository as a basis. 41 | > FTC teams should create a Fork of the FIRST-Tech-Challenge/FtcRobotController repository as a convenient way 42 | > to manage their software development process. 43 | > Thanks to the parent-child relationship, 44 | > when changes are made to the parent repository 45 | > those changes can be easily tracked and fetched/merged into the forked repository, 46 | > keeping the forked repository up to date. 47 | - The FIRST Tech Challenge documentation 48 | 49 | First, open the [FtcRobotController repository](https://github.com/FIRST-Tech-Challenge/FtcRobotController). 50 | The FtcRobotController repo is the Software Development Kit (SDK) 51 | provided by FIRST that allows you to write your own robot code. 52 | 53 | Once you have opened the repo, click the `Fork` button in the upper-right-hand corner. 54 | That will bring you to a page that looks like this: 55 | 56 | ![The fork page](../static/intro_to_git/fork.png) 57 | 58 | Under the `Owner` dropdown, select your organization (if you elected to create one), 59 | as opposed to your individual user account. 60 | Under `Repository name`, I recommend naming your repo after the current FTC season name 61 | (such as Into The Deep or CenterStage), or by the year (such as 2024). 62 | Finally, press `Create fork` to create your own copy of the SDK repository. 63 | 64 | #### 1.5 Logging into GitHub on Android Studio 65 | First, open your [GitHub token settings](https://github.com/settings/tokens), 66 | either by clicking on that link or by going to Account Settings → Developer Settings → Tokens (Classic). 67 | Press `Generate new token (classic)` at the top and that will take you to a page that looks like this: 68 | 69 | ![The token generation page](../static/intro_to_git/personal_access_token.png) 70 | 71 | For `Note`, write the use case of the token, such as "Android Studio." 72 | For `Expiration`, select `No expiration`, which may cause GitHub to warn you. 73 | For `Select scopes`, select `repo`, `workflow`, `read:org`, and `gist`. 74 | Finally, click `Generate token` and copy it. 75 | 76 | Now open Android Studio. 77 | Open your settings (under `File` then `Settings`) 78 | and then go to `Version Control` -> `GitHub`. 79 | In the top left corner of the box, press the `+` icon and `Log in with token...`, 80 | and paste in the token you just generated. 81 | 82 | ### 2. Opening Your Fork in Android Studio 83 | This step needs to be done by everyone who intends on programming for your team. 84 | 85 | First, at the top right of your new repository, press the green `Code` button. 86 | Under that tab, copy the HTTP url of your repo. 87 | 88 | Next, open Android Studio and navigate to the `New Project from Version Control` menu. 89 | To do that, do `File` -> `New` -> `Project from Version Control`, which should bring you to a menu that looks like this: 90 | 91 | ![Get from VCS menu](../static/intro_to_git/get_from_vcs.png) 92 | 93 | For the URL, paste in the link that you just copied from your repo, then press `Clone`. 94 | Android Studio will then download your project and build it through Gradle, which may take a few minutes; 95 | you can monitor this process using the progress bar in the bottom right. 96 | 97 | Once this is complete, your project is ready to use, 98 | and you can start coding as normal. 99 | 100 | 101 | ### 3. Your First Commit 102 | Now that you've made some changes, 103 | you should create a *commit* to snapshot your changes and *push* (upload) them to GitHub. 104 | To do this, press the button on the left side that looks like a line with a circle on it 105 | (just like the circles in the above image) to open the Commit menu in Android Studio. 106 | That will look like this: 107 | 108 | ![The commit menu](../static/intro_to_git/commit%20menu.png) 109 | 110 | The `Changes` section will show the files you have edited. 111 | Select the files you want to commit by clicking on the checkbox next to them, 112 | or use the checkbox in the top left to select all of them. 113 | 114 | Finally, write a commit message in the box in the lower portion of the menu to describe what you've changed. 115 | In this example, I added a `MecanumChassis` wrapper and edited some other files, 116 | so that's what I wrote in my commit message. 117 | 118 | Once you're done, 119 | press `Commit and Push...` which will commit your changes and push them to GitHub's copy of your repository. 120 | 121 | In some situations (such as when you are offline, or when a push fails), 122 | you may also prefer to just hit the `Commit` button to save an offline snapshot of your changes, 123 | and then later click your branch title in the top right, which displays the following options: 124 | 125 | ![The push icon](../static/intro_to_git/push_button.png) 126 | 127 | Click `Push...`, and then `Push` in the bottom right of the menu that comes up after that. 128 | 129 | ### 4. Pulling from GitHub 130 | Once one person has committed a change, 131 | the other programmers on your team will want to download or *pull* those changes from GitHub. 132 | To do this, click on your branch and then the `Update Project` icon or button in the top left, as shown below: 133 | 134 | ![The pull icon](../static/intro_to_git/update_project.png) 135 | 136 | This will ask you whether you want to Merge or to Rebase the incoming changes. 137 | Merging is simpler, so we will explain it here; select it and hit OK. 138 | 139 | Most of the time that will be all that is necessary to download all the incoming changes, and 140 | you will immediately be able to resume coding. 141 | However, occasionally when multiple people edit the same file at the same time, a Merge Conflict can occur. 142 | This can appear as a Conflict pop up as shown below. 143 | 144 | ![The Conflict pop up](../static/intro_to_git/merge_conflicts_dialog_dark.png) 145 | 146 | See the [official JetBrains documentation](https://www.jetbrains.com/help/idea/resolve-conflicts.html) 147 | for what to do in this scenario. 148 | Make sure to commit after the merge is complete. 149 | 150 | ## Additional Features 151 | 152 | ### Updating from the FIRST SDK 153 | Throughout the season, the FIRST SDK sometimes updates to new versions. 154 | To incorporate these changes into your codebase, 155 | we need to add the SDK as a remote repository and then pull in its changes. 156 | 157 | First, open a terminal using the button in the bottom left. 158 | 159 | Next, the first time you update, run this command in the terminal: 160 | `git remote add sdk https://github.com/FIRST-Tech-Challenge/FtcRobotController/` 161 | This will add the FIRST SDK as a remote repository named `sdk`. 162 | You will only have to do this once. 163 | 164 | Next, each time you want to update, run this command: `git pull sdk master --no-rebase` 165 | This will pull the changes from the `master` branch of the `sdk` remote repository. 166 | We use `--no-rebase` here to ensure that we merge instead of rebasing. 167 | 168 | Finally, make a new commit to incorporate the changes into your repository. 169 | There will very likely be merge conflicts, review step 4 to learn how to deal with those. 170 | 171 | ### Creating Branches 172 | A branch allows you to separate your codebase into multiple versions, 173 | which can be developed individually and combined later. 174 | Each branch can have its own set of commits. 175 | In the following image, each circle represents a commit in the branch. 176 | 177 | ![Branches and Commits](../static/intro_to_git/branches.png) 178 | 179 | Some teams prefer to create a new branch for each feature that they create. 180 | To do that in AS, in the top menu to go `Git` -> `New Branch`, and type in the name of that feature. 181 | AS will automatically *checkout* that branch, meaning all future commits from your client will be to that branch. 182 | 183 | ### Merging Branches 184 | To merge changes from one branch onto another through Android Studio, open the Git menu as shown below. 185 | 186 | ![Git menu](../static/intro_to_git/git_menu.png) 187 | 188 | Now, right-click on the branch you intend on merging from and press `Merge origin/ into `. 189 | This will simply update those files in your local copy of the code with the changes from the other branch. 190 | Note that this can also lead to Merge Conflicts as explained in step 4. 191 | After the merge is complete, make sure to commit the new combined code. 192 | 193 | 194 | It is also possible to merge changes online through GitHub using Pull Requests. 195 | Pull Requests also allow others to easily review your changes. 196 | 197 | At this point, we're going back to the GitHub website. 198 | Open your repository and hit the `Pull Requests` tab in the top left, 199 | which will open a page that looks somewhat like this: 200 | 201 | ![Pull requests](../static/intro_to_git/pull_request.png) 202 | 203 | Make sure that both repositories are the same (your repo). 204 | Then, for base, select `master`, and for `compare` select whatever branch you were working with. 205 | Press `Create pull request` and type the name and description of the commit(s) you are working with, 206 | and then press `Create pull request` again. 207 | 208 | At this point, GitHub will automatically determine if there are merge conflicts. 209 | See [GitHub's official documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-on-github) 210 | for information on how to resolve them, if they occur. 211 | Once any conflicts are resolved, 212 | and you are ready to merge the branches (potentially after getting approval from your team), 213 | select the `Merge pull request` button to accept the pull request. 214 | -------------------------------------------------------------------------------- /src/common_ds_errors/npe_at_init/npe_at_init.md: -------------------------------------------------------------------------------- 1 | # [`NullPointerException`](https://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html) on initialization 2 | 3 | NullPointerExceptions are common errors that occur when code runs in the wrong order. Here, we will cover how and why NPEs happen in general, as well as some common FTC-specific issues. 4 | 5 | ## Ingredients 6 | 7 | 1. A limited knowledge of Java. 8 | 2. A desire to learn, debug, or solve problems! 9 | 10 | ## Quick Links: Common Issues 11 | [Hardware Devices in OpModes](./npe_at_init.html#common-issues-hardware-devices-in-opmodes) 12 | [Hardware Devices in External Classes](./npe_at_init.html#common-issues-hardware-devices-in-external-classes) 13 | [BlocksCompanion hardwareMap and telemetry](./npe_at_init.html#common-issues-blockscompanion-hardwaremap-and-telemetry) 14 | 15 | ## The Recipe 16 | 17 | ### What does it look like? 18 | The general format of the error is: 19 | ``` 20 | java.lang.NullPointerException: Attempt to invoke [...] on a null object reference 21 | ``` 22 | 23 | On the Driver Station, you may see a stacktrace similar to this: 24 | 25 | ![A stacktrace of the Driver Station with a NPE.](./npe_exception.jpg) 26 |
A stacktrace of the Driver Station with a NPE.
27 | 28 | ### Why does this happen? 29 | First, we need to understand more about how Java works a little more in depth. 30 | > This section is a little lengthy, feel free to scroll below for the solution. 31 | 32 | When we program in Java, we have expressions, which have a certain **type**. 33 | The type tells us about the properties of said expression. 34 | 35 | This lets us add `int`s, set the power of a `DcMotor`, or check if a `boolean` is true! 36 | 37 | Type systems also give us a degree of validity; we can't add servos to booleans. 38 | 39 | > The following is a very generalized description with oversights, but is sufficient for conceptual understanding. 40 | 41 | In Java, there are two categories of types: 42 | - Primitive Types 43 | - Primitives are not objects, and do not have methods, only a value. 44 | - Primitives are passed by value. 45 | - `int`, `double`, and `boolean`, are examples of primitives. 46 | - Reference Types 47 | - All types that extend `Object` are passed by reference, and hence, reference types. 48 | - All objects are passed by reference. 49 | - `class`es, `interface`s, `enum`s, arrays 50 | 51 | > #### **What does it mean to pass/store an object by value or reference?** 52 | > 53 | > **Storing by Value:** 54 | > - You are storing the actual value of the variable in memory. 55 | > - This means that when you assign one variable to another, a copy of the value is made. 56 | > - Changes to one variable do not affect the other. 57 | > - ```java 58 | > int bobMoney = 20; 59 | > int jeffMoney = bobMoney; // "jeffMoney" gets the value of "bobMoney", not a reference to "bobMoney" 60 | > 61 | > jeffMoney = 10; // changing "jeffMoney" does not affect "bobMoney" 62 | > 63 | > System.out.println(bobMoney); // 20 64 | > ``` 65 | > **Storing by Reference:** 66 | > - You are storing a reference or memory address to the location where the actual data is stored. 67 | > - This means that when you assign one variable to another, they both point to the same memory location. 68 | > - Changes to one variable will affect the other because they both refer to the same data. 69 | > - ```java 70 | > Person bob = new Person("bob", 18); 71 | > Person anon = bob; // "anon" now refers to the same object as "bob" 72 | > 73 | > System.out.println(bob.getAge()); // will print 18 74 | > 75 | > anon.setAge(21); // changing "anon" also changes "bob" 76 | > 77 | > System.out.println(bob.getAge()); // will print 21 78 | > ``` 79 | 80 | `null` really refers to a null **reference**. This means any `Object` can have a `null` value. 81 | 82 | Any uninitialized `Object` has no reference to point to; a null reference, or `null`. 83 | NPEs occur when you try to use the typed properties of an object while it points to nothing. 84 | This is so no undefined behavior occurs. 85 | 86 | Java does not provide any means of "null-safety", and so it is the responsibility of the programmer to check for and handle potential null values. 87 | 88 | FTC specific examples include trying to access the `hardwareMap` at instantiation, or just never assigning a value to a `HardwareDevice`. 89 | 90 | ### hardwareMap and telemetry 91 | 92 | It is important to note that NPEs are a very common, generic exception. However, the most common causes in FTC are due to the way `hardwareMap` and `telemetry` work. This section will detail the way that `hardwareMap` and `telemetry` work, why it's so easy to get them wrong, and how to fix it. 93 | 94 | Whenever you write an `OpMode`, you use the `telemetry` and `hardwareMap` objects all the time! 95 | 96 | ```java 97 | // Instantiate drive motors 98 | DcMotor frontLeft = hardwareMap.get(DcMotor.class, "frontLeft"); 99 | ``` 100 | *A generic example of using `hardwareMap`.* 101 | 102 | However, these objects have to come from somewhere, and in fact they do! They are both created within the `OpMode` itself. This means that **you cannot directly access `hardwareMap` OR `telemetry` from outside an OpMode.** This is a very common issue, and typically the biggest cause of NullPointerExceptions. 103 | 104 | There is actually even another layer to this - while `telemetry` can be accessed anywhere within the `OpMode`, `hardwareMap` must only be accessed after the `OpMode` has started running. 105 | 106 | *Technical details: `hardwareMap` and `telemetry` are both from `OpModeInternal` (which both `OpMode` and `LinearOpMode` inherit). `telemetry` is instantiated on class construction, whereas `hardwareMap` is instantiated as soon as the `OpMode` is run.* 107 | 108 | ### Common Issues: Hardware Devices in OpModes 109 | 110 | Hardware devices (anything that requires `hardwareMap`) are **NOT** accessible at class instantiation; that is, one **cannot** do the following: 111 | 112 | ```java 113 | @TeleOp 114 | public class Testing extends OpMode { 115 | private DcMotor motor = hardwareMap.get(DcMotor.class, "motor"); // this will cause a NullPointerException because hardwareMap isn't defined until init()! 116 | 117 | @Override 118 | public void init() { } // it's always a red flag if the init is empty! TH= 119 | 120 | @Override 121 | public void loop() { 122 | motor.setPower(1.0); 123 | } 124 | } 125 | ``` 126 | 127 | Hardware devices only start to become accessible during and after `init()` in `OpMode`s and within `runOpMode()` in `LinearOpMode`s. 128 | Therefore, if you're using an `OpMode`, you should be instantiating (creating) your hardware devices in `init()`. In a `LinearOpMode`, hardware devices should be instatiated in `runOpMode()`, and before `waitForStart()`. 129 | 130 | ```java 131 | @TeleOp 132 | public class Testing extends OpMode { 133 | private DcMotor motor; 134 | 135 | @Override 136 | public void init() { 137 | motor = hardwareMap.get(DcMotor.class, "motor"); // hardwareMap is defined here, so this won't cause an error! 138 | // any other hardware device instantiations should also go here 139 | } 140 | 141 | @Override 142 | public void loop() { 143 | motor.setPower(1.0); 144 | } 145 | } 146 | ``` 147 | 148 | ### Common Issues: Hardware Devices in External Classes 149 | 150 | Let's say you have a separate class where you want to access either `hardwareMap` or `telemetry`, or both. For example, we might have an `Arm` class that controls a simple rotating arm. 151 | 152 | ```java 153 | public class Arm { 154 | private DcMotor armMotor; 155 | 156 | /** 157 | * Tilts the arm using raw motor power. 158 | * @param power The motor power. 159 | */ 160 | public void tilt(double power) { 161 | armMotor.setPower(power); 162 | } 163 | } 164 | ``` 165 | 166 | Now, you might notice in the above code that the value of `armMotor` is never set to anything. This will cause a `NullPointerException`! To prevent this, we need to assign `armMotor` a value. Typically, we'd do this using `hardwareMap`. However, if you recall, `hardwareMap` isn't defined outside of `OpMode`s! So, what to do? Well, the idea is actually rather simple: when creating an `Arm`, we'll ask for an instance of `hardwareMap` that we can then use to define `armMotor`. Since `Arm` is created in an `OpMode`, `hardwareMap` will be defined! 167 | 168 | ```java 169 | public class Arm { 170 | private DcMotor armMotor; 171 | 172 | /** 173 | * @param hardwareMap The hardwareMap instance from an OpMode. 174 | */ 175 | public Arm(HardwareMap hardwareMap) { 176 | armMotor = hardwareMap.get(DcMotor.class, "armMotor"); 177 | } 178 | 179 | /** 180 | * Tilts the arm using raw motor power. 181 | * @param power The motor power. 182 | */ 183 | public void tilt(double power) { 184 | armMotor.setPower(power); 185 | } 186 | } 187 | ``` 188 | 189 | Now that we have our `Arm` class, this is what an `OpMode` would look like: 190 | 191 | ```java 192 | @TeleOp 193 | public class ArmTest extends OpMode { 194 | private Arm arm; 195 | 196 | @Override 197 | public void init() { 198 | arm = new Arm(hardwareMap); // we still need to instantiate the arm in init(), since hardwareMap isn't defined before then 199 | } 200 | 201 | @Override 202 | public void loop() { 203 | arm.tilt(gamepad1.right_trigger - gamepad1.left_trigger); // use triggers to move arm up and down 204 | } 205 | } 206 | ``` 207 | 208 | We can also do the same thing for `telemetry`: 209 | 210 | ```java 211 | private Telemetry telemetry; 212 | 213 | public Arm(HardwareMap hardwareMap, Telemetry opModeTelemetry) { 214 | armMotor = hardwareMap.get(DcMotor.class, "armMotor"); 215 | telemetry = opModeTelemetry; 216 | } 217 | ``` 218 | 219 | ...and we now have an `Arm` class that can access both `hardwareMap` and `telemetry`! 220 | 221 | ### Common Issues: BlocksCompanion hardwareMap and telemetry 222 | 223 | Remember when we mentioned how `hardwareMap` and `telemetry` aren't defined outside of an `OpMode`? While this is true, there is still a way you can make your code *think* it can access `hardwareMap` and `telemetry`. Doing this will cause all of the NullPointerException problems with none of the helpful red squiggly lines. 224 | 225 | ```java 226 | package org.firstinspires.ftc.teamcode; 227 | 228 | import static org.firstinspires.ftc.robotcore.external.BlocksOpModeCompanion.hardwareMap; 229 | import static org.firstinspires.ftc.robotcore.external.BlocksOpModeCompanion.telemetry; 230 | 231 | import com.qualcomm.robotcore.hardware.DcMotor; 232 | 233 | public class Arm { 234 | private DcMotor armMotor; 235 | 236 | public Arm() { 237 | armMotor = hardwareMap.get(DcMotor.class, "armMotor"); 238 | } 239 | 240 | /** 241 | * Tilts the arm using raw motor power. 242 | * @param power The motor power. 243 | */ 244 | public void tilt(double power) { 245 | armMotor.setPower(power); 246 | telemetry.addData("Arm tilt power", power); // log tilt power in telemetry 247 | } 248 | } 249 | ``` 250 | 251 | What's wrong here? You might notice how our constructor for `Arm` no longer has a parameter for `hardwareMap`, yet we can still use `hardwareMap` somehow! Similarly, we are also using `telemetry` without ever even defining it! This bug is incredibly sneaky. Normally, we don't pay a ton of attention to imports, but here the imports are exactly what matter. Let's isolate the important imports: 252 | 253 | ```java 254 | import static org.firstinspires.ftc.robotcore.external.BlocksOpModeCompanion.hardwareMap; 255 | import static org.firstinspires.ftc.robotcore.external.BlocksOpModeCompanion.telemetry; 256 | ``` 257 | 258 | These two imports are incredibly nasty. The `hardwareMap` and `telemetry` above are not actually meant for us, but actually for Blocks users! This is, behind the scenes, what Blocks OpModes use. Since we are not using Blocks, these imports don't work. Luckily, although the bug is nasty, the solution is rather simple - get rid of the imports and pass `hardwareMap` and `telemetry` in like we did in the previous section! 259 | 260 | *Last updated: 2024-11-27* -------------------------------------------------------------------------------- /theme/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title }} 7 | {{#if is_print }} 8 | 9 | {{/if}} 10 | {{#if base_url}} 11 | 12 | {{/if}} 13 | 14 | 15 | 16 | {{> head}} 17 | 18 | 19 | 20 | 21 | 22 | {{#if favicon_svg}} 23 | 24 | {{/if}} 25 | {{#if favicon_png}} 26 | 27 | {{/if}} 28 | 29 | 30 | 31 | {{#if print_enable}} 32 | 33 | {{/if}} 34 | 35 | 36 | 37 | {{#if copy_fonts}} 38 | 39 | {{/if}} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{#each additional_css}} 48 | 49 | {{/each}} 50 | 51 | {{#if mathjax_support}} 52 | 53 | 55 | {{/if}} 56 | 57 | 58 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | 83 | 84 | 85 | 99 | 100 | 101 | 102 | 103 | 119 | 120 | 130 | 131 |
132 | 133 |
134 | {{> header}} 135 | 136 | 195 | 196 | {{#if search_enabled}} 197 | 208 | {{/if}} 209 | 210 | 211 | 218 | 219 |
220 |
221 | {{{ content }}} 222 |
223 | 224 | 242 |
243 |
244 | 245 | 260 | 261 |
262 | 263 | {{#if live_reload_endpoint}} 264 | 265 | 280 | {{/if}} 281 | 282 | {{#if google_analytics}} 283 | 284 | 306 | {{/if}} 307 | 308 | {{#if playground_line_numbers}} 309 | 312 | {{/if}} 313 | 314 | {{#if playground_copyable}} 315 | 318 | {{/if}} 319 | 320 | {{#if playground_js}} 321 | 322 | 323 | 324 | 325 | 326 | {{/if}} 327 | 328 | {{#if search_js}} 329 | 330 | 331 | 332 | {{/if}} 333 | 334 | 335 | 336 | 337 | 338 | 339 | 341 | 342 | 343 | 344 | 345 | 346 | {{#each additional_js}} 347 | 348 | {{/each}} 349 | 350 | {{#if is_print}} 351 | {{#if mathjax_support}} 352 | 359 | {{else}} 360 | 365 | {{/if}} 366 | {{/if}} 367 | 368 |
369 | 370 | 371 | -------------------------------------------------------------------------------- /src/roadrunner_10/complete_trajectorybuilder_reference.md: -------------------------------------------------------------------------------- 1 | # Complete TrajectoryBuilder Reference 2 | 3 | ### Ingredients 4 | 5 | 1. A fully tuned [Road Runner 1.0](https://rr.brott.dev/docs/) setup _**or**_ [MeepMeep for Road Runner 1.0](https://github.com/acmerobotics/MeepMeep) 6 | 7 | ### The Problem 8 | 9 | The current [TrajectoryBuilder Reference](https://rr.brott.dev/docs/v1-0/builder-ref/) in the official [Road Runner 1.0](https://rr.brott.dev/docs/) docs only has a few TrajectoryBuilder methods, and does not explain them very well in depth. This is a complete reference for more methods in the TrajectoryBuilder class for [Road Runner 1.0](https://rr.brott.dev/docs/). 10 | 11 | --- 12 | 13 | ## TrajectoryBuilder Reference 14 | 15 | ### Table of Contents 16 | 17 | #### [Path Primitives:](../roadrunner_10/complete_trajectorybuilder_reference.md#path-primitives-1) 18 | 1. [`waitSeconds(double: seconds)`](../roadrunner_10/complete_trajectorybuilder_reference.md#waitsecondsdouble-seconds) 19 | 2. [`turn(Math.toRadians(double: degrees))`](../roadrunner_10/complete_trajectorybuilder_reference.md#turnmathtoradiansdouble-degrees) 20 | 3. [`turnTo(Math.toRadians(double: degrees))`](../roadrunner_10/complete_trajectorybuilder_reference.md#turntomathtoradiansdouble-degrees) 21 | 4. [`setTangent(double: r)`](../roadrunner_10/complete_trajectorybuilder_reference.md#settangentdouble-r) 22 | 5. [`setReversed(boolean: reversed)`](../roadrunner_10/complete_trajectorybuilder_reference.md#setreversedboolean-reversed) 23 | 6. [`.strafeTo(new Vector2d(double: x, double: y))` & `.strafeToConstantHeading(new Vector2d(x: double, y: double))`](../roadrunner_10/complete_trajectorybuilder_reference.md#strafetonew-vector2ddouble-x-double-y--strafetoconstantheadingnew-vector2dx-double-y-double) 24 | 7. [`strafeToLinearHeading(new Vector2d(x, y), Math.toRadians(heading))`](../roadrunner_10/complete_trajectorybuilder_reference.md#strafetolinearheadingnew-vector2dx-y-mathtoradiansheading) 25 | 8. [`strafeToSplineHeading(new Vector2d(x, y), Math.toRadians(heading))`](../roadrunner_10/complete_trajectorybuilder_reference.md#strafetosplineheadingnew-vector2dx-y-mathtoradiansheading) 26 | 9. [`lineToX(x: double) & .lineToXConstantHeading(x: double)`](../roadrunner_10/complete_trajectorybuilder_reference.md#linetoxx-double--linetoxconstantheadingx-double) 27 | 10. [`lineToY(y: double) & .lineToYConstantHeading(y: double)`](../roadrunner_10/complete_trajectorybuilder_reference.md#linetoyy-double--linetoyconstantheadingy-double) 28 | 11. [`splineTo(new Vector2d(x, y), tangent)`](../roadrunner_10/complete_trajectorybuilder_reference.md#splinetonew-vector2dx-y-tangent--start-heading-is--fracpi2--radians) 29 | 30 | #### [Heading Primitives:](../roadrunner_10/complete_trajectorybuilder_reference.md#heading-primitives-1) 31 | 12. [`Tangent Heading (default)`](../roadrunner_10/complete_trajectorybuilder_reference.md#tangent-heading-default) 32 | 13. [`Constant Heading`](../roadrunner_10/complete_trajectorybuilder_reference.md#constant-heading) 33 | 14. [`Linear Heading`](../roadrunner_10/complete_trajectorybuilder_reference.md#linear-heading) 34 | 15. [`Spline Heading`](../roadrunner_10/complete_trajectorybuilder_reference.md#spline-heading) 35 | 36 | ### Path Primitives 37 | 38 | The begin pose is the origin `(0,0)` with a heading of \\( \frac{\pi}{6} \\) radians, with the exception of [`splineTo(new Vector2d(x, y), tangent)`](../roadrunner_10/complete_trajectorybuilder_reference.md#splinetonew-vector2dx-y-tangent--start-heading-is--fracpi2--radians), which has a heading of \\( \frac{\pi}{2} \\). 39 | 40 | 41 | #### `waitSeconds(double: seconds)` 42 | 43 | > 🚨 **WARNING:** 🚨 44 | > Ensure that you are using `waitSeconds()` and not `wait()`. All Java objects have a `wait()` function which causes the current thread to wait until another thread invokes a `notify()` or `notifyAll()` method. See further details in the [Oracle JavaDoc](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait()). We don't care for this function, but it does show up in intellisense. Make sure you are using the `waitSeconds()` function instead of `wait()`. 45 | 46 | ```java 47 | {{#rustdoc_include BuilderReference.java:1:4}} 48 | ``` 49 |
50 | 53 |
54 | 55 | 77 | --- 78 | 79 | #### `turn(Math.toRadians(double: degrees))` 80 | 81 | ```java 82 | {{#rustdoc_include BuilderReference.java:8:14}} 83 | ``` 84 | 85 |
86 | 89 |
90 | 91 | 113 | 114 | > **Why Radians?** 115 | > You may have noticed that we are turning by \\( \frac{\pi}{6} \\) radians instead of 30 degrees. 116 | > This is because Road Runner 1.0's units are inches and radians by default. To use degrees, we can convert degrees to radians by using Java's `Math.toRadians(degrees)` 117 | > 118 | > Example: 119 | > `Math.toRadians(90)` converts 90 degrees to radians. 90 degrees is the same as \\( \frac{\pi}{2} \\) radians. 120 | 121 | --- 122 | 123 | #### `turnTo(Math.toRadians(double: degrees))` 124 | 125 | ```java 126 | {{#rustdoc_include BuilderReference.java:18:25}} 127 | ``` 128 | 129 |
130 | 133 |
134 | 135 | 157 | 158 | --- 159 | 160 | #### `setTangent(double: r)` 161 | 162 | ```java 163 | {{#rustdoc_include BuilderReference.java:29:32}} 164 | ``` 165 | >**Tip!** 166 | > 167 | >You can learn more about tangents in RoadRunner [here](https://rr.brott.dev/docs/v1-0/guides/tangents/). 168 | 169 | --- 170 | 171 | #### `setReversed(boolean: reversed)` 172 | 173 | ```java 174 | {{#rustdoc_include BuilderReference.java:36:43}} 175 | ``` 176 | 177 |
178 | 181 |
182 | 183 | 205 | 206 | ```java 207 | {{#rustdoc_include BuilderReference.java:47:50}} 208 | ``` 209 | 210 |
211 | 214 |
215 | 216 | 238 | 239 | ```java 240 | {{#rustdoc_include BuilderReference.java:54:63}} 241 | ``` 242 | 243 | --- 244 | 245 | #### `.strafeTo(new Vector2d(double: x, double: y))` & `.strafeToConstantHeading(new Vector2d(x: double, y: double))` 246 | 247 | ```java 248 | {{#rustdoc_include BuilderReference.java:67:72}} 249 | ``` 250 | 251 |
252 | 255 |
256 | 257 | 279 | 280 | --- 281 | 282 | #### `.strafeToLinearHeading(new Vector2d(x, y), Math.toRadians(heading))` 283 | 284 | ```java 285 | {{#rustdoc_include BuilderReference.java:76:79}} 286 | ``` 287 | 288 |
289 | 292 |
293 | 294 | 316 | 317 | --- 318 | 319 | #### `.strafeToSplineHeading(new Vector2d(x, y), Math.toRadians(heading))` 320 | 321 | ```java 322 | {{#rustdoc_include BuilderReference.java:83:86}} 323 | ``` 324 | 325 |
326 | 329 |
330 | 331 | 353 | 354 | > **What is the difference between spline interpolation and linear interpolation?** 355 | > - Interpolation is a method of finding new data points (angle heading) in between two given data points (initial heading and final heading). 356 | > - Linear interpolation means that the robot interpolates its heading and turns at a constant, linear rate, from start to the end of the trajectory. 357 | > - Spline interpolation is the opposite, as the robot turns at a non-linear rate. 358 | 359 | --- 360 | 361 | #### `lineToX(x: double)` & `.lineToXConstantHeading(x: double)` 362 | 363 | > 🚨 **WARNING:** 🚨 364 | > It is **HIGHLY RECOMMENDED** to use [`.strafeTo()`](../roadrunner_10/complete_trajectorybuilder_reference.md) instead of any `lineTo()`'s! 🚨 365 | 366 | ```java 367 | {{#rustdoc_include BuilderReference.java:90:95}} 368 | ``` 369 | 370 |
371 | 374 |
375 | 376 | 398 | 399 | --- 400 | 401 | #### `lineToY(y: double)` & `.lineToYConstantHeading(y: double)` 402 | 403 | > 🚨 **WARNING:** 🚨 404 | > It is **HIGHLY RECOMMENDED** to use [`.strafeTo()`](../roadrunner_10/complete_trajectorybuilder_reference.md) instead of any `lineTo()`'s! 🚨 405 | 406 | ```java 407 | {{#rustdoc_include BuilderReference.java:99:104}} 408 | ``` 409 | 410 |
411 | 414 |
415 | 416 | 438 | 439 | --- 440 | 441 | #### `splineTo(new Vector2d(x, y), tangent)` | Start heading is \\( \frac{\pi}{2} \\) radians 442 | 443 | ```java 444 | {{#rustdoc_include BuilderReference.java:108:110}} 445 | ``` 446 | 447 |
448 | 451 |
452 | 453 | 475 | 476 | --- 477 | 478 | ### Heading Primitives 479 | 480 | The begin pose is the origin `(0,0)` with a heading of \\( \frac{\pi}{2} \\) radians. 481 | 482 | #### `Tangent Heading (default)` 483 | 484 | ```java 485 | {{#rustdoc_include BuilderReference.java:114:117}} 486 | ``` 487 | 488 |
489 | 492 |
493 | 494 | 516 | 517 | --- 518 | 519 | #### `Constant Heading` 520 | 521 | ```java 522 | {{#rustdoc_include BuilderReference.java:121:126}} 523 | ``` 524 | 525 |
526 | 529 |
530 | 531 | 553 | 554 | --- 555 | 556 | #### `Linear Heading` 557 | 558 | ```java 559 | {{#rustdoc_include BuilderReference.java:130:134}} 560 | ``` 561 | 562 |
563 | 566 |
567 | 568 | 590 | 591 | --- 592 | 593 | #### `Spline Heading` 594 | 595 | ```java 596 | {{#rustdoc_include BuilderReference.java:138:142}} 597 | ``` 598 | 599 |
600 | 603 |
604 | 605 | 627 | 628 | --- 629 | 630 | ## Resources 631 | 632 | - [Official Road Runner 1.0 Builder Reference](https://rr.brott.dev/docs/v1-0/builder-ref/) 633 | - [Official Road Runner 1.0 Builder Playground](https://rr.brott.dev/playground/) 634 | - [`waitSeconds()` Video Playground](https://rr.brott.dev/playground/?367582b88299b654) 635 | - [`turn()` Video Playground](https://rr.brott.dev/playground/?5a434271b239e5bc ) 636 | - [`turnTo()` Video Playground](https://rr.brott.dev/playground/?ad40438ae13740f7) 637 | - [`.setReversed(false)` Video Playground](https://rr.brott.dev/playground/?78c18fcfaa629ea1) 638 | - [`.setReversed(true)` Video Playground](https://rr.brott.dev/playground/?f13b09a93d37031c) 639 | - [`strafeTo()` / `strafeToConstantHeading()` Video Playground](https://rr.brott.dev/playground/?f49042fdf5ea3ab0) 640 | - [`.strafeToLinearHeading()` Video Playground](https://rr.brott.dev/playground/?ed5d41566d46b59f) 641 | - [`.strafeToSplineHeading()` Video Playground](https://rr.brott.dev/playground/?2df122402fceaecc) 642 | - [`.lineToX()` Video Playground](https://rr.brott.dev/playground/?24fae2ad24c95f39) 643 | - [`.lineToY()` Video Playground](https://rr.brott.dev/playground/?539e8ead2f501b29) 644 | - [`.splineTo()` Video Playground](https://rr.brott.dev/playground/?0a660e7b5fae2b70d) 645 | - [`.splineTo() (default)` Video Playground](https://rr.brott.dev/playground/?728d3c0e2e969023) 646 | - [`.splineToConstantHeading()` Video Playground](https://rr.brott.dev/playground/?31ac47a09d8222c7) 647 | - [`.splineToLinearHeading()` Video Playground](https://rr.brott.dev/playground/?b152980ce38bbdaa) 648 | - [`.splineToSplineHeading()` Video Playground](https://rr.brott.dev/playground/?9c422405d44fed70) 649 | 650 | --- 651 | --------------------------------------------------------------------------------