You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi Team 687, thank you for sharing your code with our team. Three of our software students and our software mentor all cloned your codebase, reviewed the code, and discussed it as a team to come up with this feedback. We approached the codebase as if we were new students on your team trying to understand how the code works.
Overall Thoughts
The codebase is quite large and complex. There is a lot of great code and it is very well structured. Just by reading class names and packages we could get a sense of what the robot is supposed to do. We had a bit of a tough time determining specifics of what a subsystem did because of the level of abstraction in NerdyLib. There was also a lot of unused and uncommented code in NerdyLib which made it hard to figure out what we should focus on.
Things we Liked
Command based everything - We have only used command base programming for part of our robot. It was neat to see an entire codebase where everything had a command.
Logging - We liked the extensive use of logging and logging with lambdas. It was great to see a standard for logging being used throughout the project.
ParallelRaceGroup in Auto - Using a parallel race group for stopping the intake motor after the trajectory is complete is a very elegant solution.
State Space - We have been working towards adding state space control to our robot, so it was great to see another team with code for it.
Auto Trajectory Classes - Having different classes for each auto trajectory made it very easy to understand what each trajectory was supposed to do.
Ether Joystick Sensitivity - The Ether joystick sensitivty code was very neat!
Things to Improve
NerdyLib Abstraction
As a new reader, NerdyLib contained many layers of abstraction that made it very hard to follow. The Hood was a subsystem with a single motor, but the chain of inheritance looked like this:
Each of these classes defined a small piece of functionality and it was difficult to figure out exactly what the Hood class did. We weren't even sure it was a WPI Subsystem until we followed the code 7 classes down the chain. For new students or for students coming from different teams, this abstraction would make it difficult to understand the "whole picture" of what a class does.
If the Hood is managing a single motor, it would be clearer to configure and operate the motor within the Hood class, even if it means some duplicate code in other single motor classes. If you want to reduce boilerplate, perhaps you could use utility functions to configure the motor with the arguments you need for each type of functionality?
For example, these super.* methods in the Hood class could be part of a MotorConfigurer() class, instead of part of the Hood inheritance:
This way, rather than looking through a bunch of classes to figure out how this motor is configured, you can look in one place.
Other NerdyLib Suggestions
A README with a diagram showing the various classes and what they are for would be a great step towards helping the code be more understandable.
A comment above each subsystem to describe how it worked would be helpful
Magic Numbers
The Hood class uses magic numbers instead of constants to change behavior based on distance
OJ.java should use joystick constants instead of numbers. It's tough to tell what buttons map up to which commands.
distance vs distanceWidth
We had to ask what distanceWidth was. distance is not used at all. It would be clearer to remove the distance variable and rename distanceWidth to distance.
Odometry
WPI has a built in characterization and odometry library. WPILib uses some fancy method with pose exponentials that's more accurate. See Controls Engineering in the FIRST Robotics Competition by Tyler Veness. WPILib uses a higher order approximation. Your current calcXY() is an Euler approximation so it's first order which means it'll be less accurate compared to WPILib's unless it's run at a very fast rate.
Shooter Distance Approximation
For the shooter distance to speed approximation, a spline is probably more accurate. You certainly don't need it, but given how mature your codebase is, it might be a fun project.
Miscellaneous
OI.java has large swaths of code that is commented out. Is this code necessary? Can it be removed? There is no comment saying "This code will go back in when debugging" or anything like that.
OI.java could use commented sections to better separate functionality. For example:
This way you can easily find the functionality you are looking for.
There is a lot of unused code in the robot that should probably be moved to NerdyLib rather than left in the main robot. It's a large code base and only made more complicated by sifting through unused classes.
Voltage Compensation should be less than 12V. The battery will drop when it is being used, so it's better to set your voltage compensation to a realistic value.
State Space Feedback
In SSTalonSRXPos, when there's no observer enabled, you are setting the state of the motor as:
In the case of most FRC applications, C happens to be diagonal and D happens to be a zero matrix so this works, but you should also account for when it doesn't (example: acceleration reported by an IMU is a linear combination of velocity and control, so D is not 0. That's because of the continuous-time representation of a system, a = v' is a linear combination of v and u). So just more generally, assuming C is invertible (which it almost surely will be), x_hat = inv(C) * (y - D*u) is what you should go for.
Along those lines, when updating your observer, you assume D = 0, which again may not be the case. It usually is, but it's best to be prepared and not have to make changes to your state-space library during build season! That being said, there are people working on submitting a pull request to WPILib with a state-space library, so we both have that to look forward to!
It was hard to tell whether you're using plant augmentation or just have unused variables for it, and how the plant was being augmented. In general, commenting state-space code is a very good idea since regular code is confusing enough as is, without having linear algebra and control theory concepts.
In JamaUtils, it seems like you're using a Taylor expansion to approximate the matrix exponential, along with some fancy scaling in order to make it more accurate. As you said, a Padé approximation is much better for this kind of thing. In general, it's best to not write your own matrix utilities unless it's simple stuff like your identity, diagonals,...
Luckily, WPILib already has a SimpleMatrixUtils class with an expm function, but it's written with the EJML matrix library instead of Jama. Maybe you could convert your Jama matrix into an EJML SimpleMatrix, run expm(), then convert back? We had to do something similar to take the logarithm of a SimpleMatrix, which WPILib does not have but another Java matrix library - Jeigen, which is an interface for the C library Eigen - does have. So there are ways to do it! But you said you're doing all the pre-processing in Python anyways so it doesn't really matter.
Summary
Thank you for giving us the opportunity to see your code. It was very informative to see how your team coded a robot. We learned a lot from it and we hope our feedback is helpful to you in your future robot coding. Good luck in the rest of the season!
2020 Code Review Party - Team 4183 Feedback
Hi Team 687, thank you for sharing your code with our team. Three of our software students and our software mentor all cloned your codebase, reviewed the code, and discussed it as a team to come up with this feedback. We approached the codebase as if we were new students on your team trying to understand how the code works.
Overall Thoughts
The codebase is quite large and complex. There is a lot of great code and it is very well structured. Just by reading class names and packages we could get a sense of what the robot is supposed to do. We had a bit of a tough time determining specifics of what a subsystem did because of the level of abstraction in NerdyLib. There was also a lot of unused and uncommented code in NerdyLib which made it hard to figure out what we should focus on.
Things we Liked
Things to Improve
NerdyLib Abstraction
Hood -> SingleMotorArm -> GravityAffectedMechanism -> StaticFrictionMechanism -> SingleMotorMechanism -> SingleMotorMechanism -> SmartMotorControllerSubsystem -> AbstractSingleMotor -> SusbsystemBaseEach of these classes defined a small piece of functionality and it was difficult to figure out exactly what the Hood class did. We weren't even sure it was a WPI Subsystem until we followed the code 7 classes down the chain. For new students or for students coming from different teams, this abstraction would make it difficult to understand the "whole picture" of what a class does.
If the Hood is managing a single motor, it would be clearer to configure and operate the motor within the Hood class, even if it means some duplicate code in other single motor classes. If you want to reduce boilerplate, perhaps you could use utility functions to configure the motor with the arguments you need for each type of functionality?
For example, these super.* methods in the Hood class could be part of a MotorConfigurer() class, instead of part of the Hood inheritance:
This way, rather than looking through a bunch of classes to figure out how this motor is configured, you can look in one place.
Other NerdyLib Suggestions
Magic Numbers
distance vs distanceWidth
Odometry
Shooter Distance Approximation
Miscellaneous
This way you can easily find the functionality you are looking for.
State Space Feedback
In the case of most FRC applications, C happens to be diagonal and D happens to be a zero matrix so this works, but you should also account for when it doesn't (example: acceleration reported by an IMU is a linear combination of velocity and control, so D is not 0. That's because of the continuous-time representation of a system,
a = v'is a linear combination of v and u). So just more generally, assuming C is invertible (which it almost surely will be),x_hat = inv(C) * (y - D*u)is what you should go for.Along those lines, when updating your observer, you assume
D = 0, which again may not be the case. It usually is, but it's best to be prepared and not have to make changes to your state-space library during build season! That being said, there are people working on submitting a pull request to WPILib with a state-space library, so we both have that to look forward to!It was hard to tell whether you're using plant augmentation or just have unused variables for it, and how the plant was being augmented. In general, commenting state-space code is a very good idea since regular code is confusing enough as is, without having linear algebra and control theory concepts.
In JamaUtils, it seems like you're using a Taylor expansion to approximate the matrix exponential, along with some fancy scaling in order to make it more accurate. As you said, a Padé approximation is much better for this kind of thing. In general, it's best to not write your own matrix utilities unless it's simple stuff like your identity, diagonals,...
Luckily, WPILib already has a SimpleMatrixUtils class with an expm function, but it's written with the EJML matrix library instead of Jama. Maybe you could convert your Jama matrix into an EJML SimpleMatrix, run expm(), then convert back? We had to do something similar to take the logarithm of a SimpleMatrix, which WPILib does not have but another Java matrix library - Jeigen, which is an interface for the C library Eigen - does have. So there are ways to do it! But you said you're doing all the pre-processing in Python anyways so it doesn't really matter.
Summary
Thank you for giving us the opportunity to see your code. It was very informative to see how your team coded a robot. We learned a lot from it and we hope our feedback is helpful to you in your future robot coding. Good luck in the rest of the season!