VingVing Robot LogoVingVing Robot

Documentation v0.0.3

VingVing Robot Docs

TeleOp Examples

Using VingVing Robot for driver-controlled operation

TeleOp with VingVing Robot

While VingVing Robot excels at autonomous, you can also use its drivetrain and localization features during TeleOp for field-centric driving, position tracking, and driver assists.

Example 1: Basic TeleOp

BasicTeleOp.java
package org.firstinspires.ftc.teamcode;

import com.qualcomm.robotcore.eventloop.opmode.OpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.vingvingrobot.Robot;

@TeleOp(name = "Basic TeleOp", group = "VingVing")
public class BasicTeleOp extends OpMode {
    Robot robot;

    @Override
    public void init() {
        robot = new Robot(hardwareMap, RobotConfig.class);
        robot.initializeLocalizer(hardwareMap);

        telemetry.addLine("Robot initialized");
        telemetry.addLine("Ready to start!");
    }

    @Override
    public void loop() {
        // Update localizer
        robot.update();

        // Get gamepad inputs
        double drive = -gamepad1.left_stick_y;   // Forward/backward
        double strafe = gamepad1.left_stick_x;   // Left/right
        double turn = gamepad1.right_stick_x;    // Rotation

        // Apply power scaling for better control
        double powerScale = gamepad1.right_trigger > 0.1 ? 0.4 : 1.0; // Slow mode

        // Calculate motor powers (robot-centric)
        double[] powers = calculateMecanumPowers(
            drive * powerScale,
            strafe * powerScale,
            turn * powerScale
        );

        // Set motor powers
        robot.setDrivePowers(powers);

        // Telemetry
        telemetry.addData("Position", "(%.1f, %.1f)",
            robot.getLocalizer().getX(),
            robot.getLocalizer().getY());
        telemetry.addData("Heading", "%.1f°",
            Math.toDegrees(robot.getLocalizer().getHeading()));
        telemetry.addData("Speed Mode", powerScale == 1.0 ? "Normal" : "Slow");
    }

    private double[] calculateMecanumPowers(double drive, double strafe, double turn) {
        double lf = drive + strafe + turn;
        double rf = drive - strafe - turn;
        double lb = drive - strafe + turn;
        double rb = drive + strafe - turn;

        // Normalize powers
        double max = Math.max(Math.max(Math.abs(lf), Math.abs(rf)),
                             Math.max(Math.abs(lb), Math.abs(rb)));
        if (max > 1.0) {
            lf /= max;
            rf /= max;
            lb /= max;
            rb /= max;
        }

        return new double[]{lf, rf, lb, rb};
    }
}

Example 2: Field-Centric TeleOp

FieldCentricTeleOp.java
@TeleOp(name = "Field-Centric TeleOp")
public class FieldCentricTeleOp extends OpMode {
    Robot robot;
    boolean fieldCentric = true;

    @Override
    public void init() {
        robot = new Robot(hardwareMap, RobotConfig.class);
        robot.initializeLocalizer(hardwareMap);
    }

    @Override
    public void loop() {
        robot.update();

        // Toggle field-centric mode
        if (gamepad1.a) {
            fieldCentric = !fieldCentric;
            sleep(200); // Debounce
        }

        // Get joystick inputs
        double drive = -gamepad1.left_stick_y;
        double strafe = gamepad1.left_stick_x;
        double turn = gamepad1.right_stick_x;

        double[] powers;

        if (fieldCentric) {
            // Field-centric: joystick directions relative to field
            double heading = robot.getLocalizer().getHeading();

            // Rotate drive vector by robot heading
            double rotDrive = drive * Math.cos(-heading) - strafe * Math.sin(-heading);
            double rotStrafe = drive * Math.sin(-heading) + strafe * Math.cos(-heading);

            powers = calculateMecanumPowers(rotDrive, rotStrafe, turn);
        } else {
            // Robot-centric: joystick directions relative to robot
            powers = calculateMecanumPowers(drive, strafe, turn);
        }

        robot.setDrivePowers(powers);

        // Telemetry
        telemetry.addData("Drive Mode", fieldCentric ? "Field-Centric" : "Robot-Centric");
        telemetry.addData("Position", "(%.1f, %.1f)",
            robot.getLocalizer().getX(),
            robot.getLocalizer().getY());
        telemetry.addData("Heading", "%.1f°",
            Math.toDegrees(robot.getLocalizer().getHeading()));
        telemetry.addLine();
        telemetry.addLine("Press A to toggle drive mode");
    }

    private double[] calculateMecanumPowers(double drive, double strafe, double turn) {
        double lf = drive + strafe + turn;
        double rf = drive - strafe - turn;
        double lb = drive - strafe + turn;
        double rb = drive + strafe - turn;

        double max = Math.max(Math.max(Math.abs(lf), Math.abs(rf)),
                             Math.max(Math.abs(lb), Math.abs(rb)));
        if (max > 1.0) {
            lf /= max; rf /= max; lb /= max; rb /= max;
        }

        return new double[]{lf, rf, lb, rb};
    }
}

Example 3: TeleOp with Driver Assists

AssistedTeleOp.java
@TeleOp(name = "Assisted TeleOp")
public class AssistedTeleOp extends OpMode {
    Robot robot;
    Follower follower;

    // Preset positions (field coordinates)
    private static final Point SCORING_POSITION = new Point(6, 52, Math.toRadians(90), Point.FIELD);
    private static final Point LOADING_POSITION = new Point(48, 12, Math.toRadians(180), Point.FIELD);

    private boolean autoMode = false;

    @Override
    public void init() {
        robot = new Robot(hardwareMap, RobotConfig.class);
        robot.initializeLocalizer(hardwareMap);
        follower = new Follower(hardwareMap);
    }

    @Override
    public void loop() {
        robot.update();

        // Driver Assist: Auto-navigate to preset positions
        if (gamepad1.y && !autoMode) {
            // Go to scoring position
            navigateToPosition(SCORING_POSITION);
            autoMode = true;
        } else if (gamepad1.b && !autoMode) {
            // Go to loading position
            navigateToPosition(LOADING_POSITION);
            autoMode = true;
        } else if (gamepad1.x) {
            // Cancel auto navigation
            autoMode = false;
        }

        if (autoMode) {
            // Auto-navigate mode
            follower.update();
            robot.setDrivePowers(follower.getMotorPowers());

            if (follower.isPathComplete()) {
                autoMode = false; // Return to manual control
            }

            telemetry.addLine("🤖 AUTO-NAVIGATION");
            telemetry.addData("Progress", "%.0f%%", follower.getPathProgress() * 100);
        } else {
            // Manual control
            double drive = -gamepad1.left_stick_y;
            double strafe = gamepad1.left_stick_x;
            double turn = gamepad1.right_stick_x;

            double[] powers = calculateMecanumPowers(drive, strafe, turn);
            robot.setDrivePowers(powers);

            telemetry.addLine("👤 MANUAL CONTROL");
        }

        // Position telemetry
        telemetry.addData("Position", "(%.1f, %.1f)",
            robot.getLocalizer().getX(),
            robot.getLocalizer().getY());
        telemetry.addLine();
        telemetry.addLine("Y: Auto to Scoring | B: Auto to Loading");
        telemetry.addLine("X: Cancel Auto");
    }

    private void navigateToPosition(Point target) {
        // Get current position
        double currentX = robot.getLocalizer().getX();
        double currentY = robot.getLocalizer().getY();
        double currentHeading = robot.getLocalizer().getHeading();

        // Create path from current to target
        follower.followPath(
            new BezierLine(
                new Point(currentX, currentY, currentHeading, Point.FIELD),
                target
            ).setLinearHeadingInterpolation()
        );
    }

    private double[] calculateMecanumPowers(double drive, double strafe, double turn) {
        double lf = drive + strafe + turn;
        double rf = drive - strafe - turn;
        double lb = drive - strafe + turn;
        double rb = drive + strafe - turn;

        double max = Math.max(Math.max(Math.abs(lf), Math.abs(rf)),
                             Math.max(Math.abs(lb), Math.abs(rb)));
        if (max > 1.0) {
            lf /= max; rf /= max; lb /= max; rb /= max;
        }

        return new double[]{lf, rf, lb, rb};
    }
}

TeleOp Tips

💡 Field-Centric is Powerful

Field-centric drive makes controlling the robot much easier - pushing forward always moves toward the opponent's side, regardless of robot rotation.

💡 Use Position Tracking

Even in TeleOp, knowing your position helps drivers. Display coordinates on telemetry or use for automated positioning assists.

💡 Driver Assists Win Matches

Auto-navigation to scoring positions saves time and reduces driver error. Let drivers focus on strategy, not precise positioning.