Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a visual roadrunner path editor #134

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

nash-pillai
Copy link
Contributor

Some places it could be improved:

  • It is a little laggy
  • You cannot see the spline until the opmode starts, only the points
  • While you can use your mouse for most of the functionality, a help menu for the vim keybindings would be nice

@nash-pillai
Copy link
Contributor Author

nash-pillai commented Feb 27, 2023

I used the following opmode to run the path that is uploaded (it is in kotlin):

package org.firstinspires.ftc.teamcode

import com.acmerobotics.dashboard.message.redux.UploadPath
import com.acmerobotics.dashboard.path.DashboardPath
import com.acmerobotics.dashboard.path.HeadingType
import com.acmerobotics.dashboard.path.PathSegment
import com.acmerobotics.dashboard.path.SegmentType
import com.acmerobotics.roadrunner.geometry.Pose2d
import com.acmerobotics.roadrunner.geometry.Vector2d
import com.qualcomm.robotcore.eventloop.opmode.Autonomous
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode
import org.firstinspires.ftc.teamcode.drive.SampleMecanumDrive
import org.firstinspires.ftc.teamcode.trajectorysequence.TrajectorySequence

@DashboardPath
@Autonomous
class CustomPath : LinearOpMode() {
    companion object {
        @JvmField var dashboardPath = UploadPath(PathSegment(), arrayOf())
    }
    fun makeTrajectory(drive: SampleMecanumDrive): TrajectorySequence {
        val builder = drive.trajectorySequenceBuilder(
            Pose2d(dashboardPath.start.x, dashboardPath.start.y, dashboardPath.start.heading),
            dashboardPath.start.tangent,
        )
        drive.poseEstimate = Pose2d(dashboardPath.start.x, dashboardPath.start.y, dashboardPath.start.heading)

        for (s in dashboardPath.segments) when (s.type) {
            SegmentType.WAIT -> builder.waitSeconds(s.time)
            SegmentType.LINE -> when (s.headingType) {
                HeadingType.TANGENT -> builder.lineTo(Vector2d(s.x, s.y))
                HeadingType.CONSTANT -> builder.lineToConstantHeading(Vector2d(s.x, s.y))
                HeadingType.LINEAR -> builder.lineToLinearHeading(Pose2d(s.x, s.y, s.heading))
                HeadingType.SPLINE -> builder.lineToSplineHeading(Pose2d(s.x, s.y, s.heading))
                else -> error("Unknown HeadingType ${s.headingType}")
            }
            SegmentType.SPLINE -> when (s.headingType) {
                HeadingType.TANGENT -> builder.splineTo(Vector2d(s.x, s.y), s.tangent)
                HeadingType.CONSTANT -> builder.splineToConstantHeading(Vector2d(s.x, s.y), s.tangent)
                HeadingType.LINEAR -> builder.splineToLinearHeading(Pose2d(s.x, s.y, s.heading), s.tangent)
                HeadingType.SPLINE -> builder.splineToSplineHeading(Pose2d(s.x, s.y, s.heading), s.tangent)
                else -> error("Unknown HeadingType ${s.headingType}")
            }
            else -> error("Unknown SegmentType ${s.type}")
        }

        return builder.build()
    }
    override fun runOpMode() {
        val drive = SampleMecanumDrive(hardwareMap)
        val path = makeTrajectory(drive)

        waitForStart()
        if (isStopRequested) return
        drive.followTrajectorySequence(path)
        sleep(5000)
    }
}

@NoahBres
Copy link
Contributor

NoahBres commented Feb 27, 2023

Do you have a screen recording of this?
I've always had the idea of a visual path editor brewing in the back of my mind 👀

My own PR idea was building out primitives for a generalizable events (mouse click, drag, etc) interaction + drawing api though. It would build on the current field drawing widget which gives dash canvas draw commands. But providing a more generalizable API would imo prove to be more flexible in the future.

E.g.
Instead of a Field widget, provide a general Canvas widget (or Field widget can just be a special extension of this) that supports the current canvas operations + include an image drawing command. This way, the Dash client doesn't need to be updated for every season. Rather, the RR quickstart repo provided can just push a new image or users can just draw their own image. I do have a PR for custom image uploads though. But I think this would provide a better foundation for the future as it can also provide primitives for events (the aforementioned mouse click, drag, etc.) so that you can support a RR visual path editor but have it handled by the quickstart repo instead of the dash client.

Just an idea though. I haven't made an RFC PR since it was just ideas floating around.

@nash-pillai
Copy link
Contributor Author

I used Konva to make the field interactive. I only drew the control points of the path and I overlayed the preexisting path drawings you have. This is a picture of an example path I made when testing it:
image
Here is a video from a while ago demonstrating some of the controls:

ftc-vim.mp4

I'll make a more up-to-date video tomorrow

@rbrott
Copy link
Member

rbrott commented Mar 12, 2023

Very cool! I got it working locally, and I'm impressed.

I would normally worry about contaminating dash with "knowledge" of RR, though you've helpfully kept a RR dependency out of the patch. I don't want hermetic separation between RR and dash to obstruct a useful feature like this, and the API is general enough to work with RR 1.0.0 I think.

My main desire is to leverage the config system to store all of the path state. I imagine the Path widget could scan the config tree for classes with a particular field and shunt updates through the existing config messages. This probably involves adding lists/arrays to the config model, which is a useful and orthogonal feature people have been asking about for some time (#7!).

A general third-party component system (a la Noah's RFC) would be a great fit for this PR, but I'm not sure that yak is worth shaving yet with the current scope of the path editor.

@NoahBres
Copy link
Contributor

but I'm not sure that yak is worth shaving yet with the current scope of the path editor

Very much agree! Always for delivering value first. Just wanted to drop the idea around for others to see :)
CleanShot 2023-03-16 at 14 31 33@2x
This kind of customizable input would also be very neat to see!

Awesome work :))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants