FRC 1155 SciBorgs Programming Thread

Hello CD!

We’re team 1155, the SciBorgs, from Bronx Science. Our programming department has been working on tons of cool projects that we’d like to share progress on and potentially get feedback. This thread is going to be similar to a build thread, although not affiliated with OA.

Some context about our team:

Our programming department was almost exclusively rookies when we got back from covid last year, our code was a mess, and we had no testing. As you might expect, competitions were disastrous. However, we immediately started improving as fast as possible. By an off season in November (900+ commits on our Rapid React repository), we had a mix of motion profiling, PID, and FF being properly applied to mechanisms. We changed our code to be as pragmatic as possible, following best practices with attention to detail (still no testing though).

Future projects:

Looking to the future, we’ve started several large programming projects, which will likely be the main focus of this thread, that we hope will vastly improve our team’s capabilities and prevent a significant loss of generational knowledge in the future. These are eScout, SciLib, and SciGuides. We’re also considering switching to Kotlin for the 2024 season.

Thank you for taking the time to read this. We hope you have a nice season!

8 Likes

SciLib development so far

Initial Discussion & motor builders

After the rapid react season, it became apparent that we needed a team library to keep high quality documented code for future use. It was initially supposed to include standardized wrappers for vendor specific motors, but we later decided to deal with motors via builders, since configuration was the most useful part to standardize.

Controls

The scope later increased to include a control and filter library, adapted from #frc694’s StuyLib. It was intended to be a minimal port, avoiding some aspects that we didn’t like. I jumped to implement this and eventually got to a prototype that hypothetically would work. However, we decided that there was no clearly justifiable reason to maintain this library over WPILib’s, especially when the stated purpose was simplicity. As of now, this feature has been dropped, but we’re exploring another fun implementation of controllers using streams.

Kotlin

We decided to start testing and teaching kotlin during the 2023 off-season, however it has many features that can be taken advantage of best with a team library. Since we can still use kotlin with java in the 2023 season, we started implementing features in it.

MotorConfig

Immediately, kotlin’s named parameters and data class features drastically improved the usability of MotorConfig

Example usage for creating a drivetrain

val leftMotor = MotorConfig(neutralBehavior = NeutralBehavior.BRAKE, currentLimit = 80)
val rightMotor = leftMotor.copy(inverted = true)

val leftPorts = intArrayOf(1, 2, 3)
val rightPorts = intArrayOf(4, 5, 6)

val leftGroup = MotorControllerGroup(leftMotor.buildCanSparkMax(*leftPorts, motorType = MotorType.kBrushless))
val rightGroup = MotorControllerGroup(rightMotor.buildCanSparkMax(*rightPorts, motorType = MotorType.kBrushless))
val driveTrain = DifferentialDrive(leftGroup, rightGroup)

Streams

Leftover from the abandoned control library was a Filter functional interface, representing a chainable DoubleUnaryOperator, or (Double) -> Double function. We realized a very helpful tool for debugging would be a LoggableFilter, which would be a class that implements Filter and Sendable, allowing for the logging of all intermediary values. There was one problem with filters though, they were clunky. In every single use case, filter would be used to calculate based off of the same input every time. That’s why we cleaned up the api and replaced filters with streams, functional interfaces that extend DoubleSupplier. They support operations between streams, scalar multiplication/division, a map method to transform the stream, and a SendableStream, which would override all Stream functionality to also log the last output value of every transformation.

An example usage of streams (Log annotation is from oblog and camera is made up) looks like

@Log val xStream = vision::x.stream().log().movingAverage(5).map { MathUtil.clamp(it, -100, 100) }

In this example, log() converts the stream into a SendableStream, and movingAverage is shorthand for creating a new LinearFilter (wpilib class) and then running .map { movingAvg.calculate(it) }

Networktables would display the initial x value, the filtered value, and the clamped filtered value, so that you could locate where something is going wrong if a bug arises.

Or an inefficient velocity pid implementation that I made for fun

val error = Stream { setpoint - encoder.velocity }
val p = kp * error
val i = ki * error.integrate().map { MathUtil.clamp(it, minI, maxI) }
val d = kd * error.differentiate()
val pid = p + i + d

Note that SendableFilter is untested

I’m really curious to hear anyone’s opinion on this feature

Future

In the future, there are several large features we want to implement. Specifically, units and a coroutine based command scheduler. Not too much thought has been put into the coroutine stuff yet, but I would imagine it would be beneficial since we plan to use pose estimation in the future, and that requires lengthy calculations. One other goal with the coroutines is to make sure that the resulting code is close to the wpilib project structure, if this even is possible. Units are a lot more self explanatory, and we will begin working on them soon, assuming we have extra time during build season.

5 Likes

eScout Development

Hey all! This marks the very first post of eScout development within the Sciborgs programming head. I have a more detailed explanation on how eScout works here, but in brief terms, eScout is designed to be a web application designed for FRC strategy, available to all FRC teams that want to use it.

Initial motivations

Prior to the idea of eScout, the Sciborgs used paper scouting, and entered the data in manually into a spreadsheet after each day. This method of scouting is much better than having none at all, but it contained a lot of unnecessary tediousness that could be addressed with a little code.

The goal for eScout was to initially be a simple web application (for easy accessibility) that could take in match scout inputs, and to display important statistics and graphs for those inputs. It was especially important that the match scouting UI was easy and intuitive to use, because it makes it easier for the scouter to record more accurate data easily. Additionally, it was also important that the app had data filters, to analyze and identify trends on certain characteristics that we were interested in (for example, say we wanted to look for a good defense team, it would be very convenient if we could filter all our scouting data by teams that have the highest DPR).

Implementation details

Currently, eScout runs off of the T3 stack. At the time of making eScout, I was working with a similar stack for another group project, and I fell in love with using tRPC and Prisma as a backend. Since a lot of data within eScout is interconnected with other large chunks of data, Prisma was especially useful for quickly defining relationships. Currently, the source code for eScout can be found here (but to be honest I think its pretty messy).

Some background

Currently, the latest public version of eScout can be found here. The Sciborgs used this version during Brunswick Eruption 2022, and it was overall a pretty successful run. However, there were definitely a lot of hiccups that occurred, and I hope to solve all of them in eScout’s next release.

Some of the issues that need to be addressed:

  • Small UI bugs for mobile
    Honestly, this should have been fixed before, but I accidentally missed a few changes to make it look pretty. Some of the graphs kept extending beyond the screen border, which was definitely not good.
  • No offline functionality
    Since eScout was built to be a web-app, some form of Wi-Fi connection was needed to successfully upload match scouting data to our database. Brunswick Eruption barely had any service, but luckily my phone had 5G data, so we were able to narrowly avoid not being able to have any scouting data for the playoffs.
  • Filters were very limited
    Although filters were intended to be a big part of eScout, the only filters that made it into this version were team number filters and competition name filters. in the future, I intend to have eScout have filters that allow you to sort teams based on numerical statistics, which is a lot more important for identifying feature-specific teams.
  • Pit scouting forms weren’t exactly the best
    eScout has a built in functionality to create custom pit scouting forms. Although Google Forms is a significantly better alternative to handling this kind of thing, it would be nice if eScout grouped up submitted pit scouts and match scouts together to provide a nice experience where all your data is in one place. However, this was not implemented in time before Brunswick, so all we really ended up having was a janky Google Forms that sort of worked, but had questionable reliability.
  • Match scouting forms are decided by the programmer
    This point is arguably one of the most important changes coming to eScout, which is the ability to create custom match forms. Currently, the match scouting form on eScout is hardcoded and implemented by the programmer (me). However, the developer shouldn’t be the one deciding what data is important to scout, both for my team and for any other team that chooses to use eScout. The goal for the next release of eScout is to allow teams to create their own match forms consisting of the UI components that are currently shown on eScout’s match scout page (along with many more). This feature is currently in the works, and will hopefully be getting another post on this thread before kickoff begins.

That’s all for the introductory post. The goal is to have this project fully finished before competitions begin rolling out. Feel free to ask questions or leave suggestions, I’d love to read them and implement them!

7 Likes

That’s a really cool way to use Sendable!

1 Like

Thank you so much, it’s a bit of a jank implementation, but it works!

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.