Advice on shooting while moving

I am looking for advice on how to program our robot to shoot while moving. Our robot has a turret flywheel shooter with an adjustable hood. We can already shoot while stationary by using the limelight to determine the angle and distance to goal (and thus flywheel speed, turret angle, and hood angle; we have done tests to determine the ideal flywheel speed and hood angle for several distances and we interpolate the rest). I would like to incorporate the robot’s velocity into consideration when determining these three variables.

As for thinking of this as a physics problem, we know the robot’s distance from the goal (determined by limelight & some code utils), as well as the robot’s velocity (determined by WPILib’s ChassisSpeeds class). What makes this problem difficult is how little information we have about the ball. Since its launch speed, angle, and time in the air are all determined by the distance to the goal (which determines flywheel speed and hood angle), I’m thinking I could have another data table that relates distance from goal to time in the air. Given these quantities, I could determine the turret angle that should result in the ball going in the direction of the goal.

However, even if the ball is now going in the right direction, it will still overshoot or undershoot the goal if the robot’s velocity component in the y direction is nonzero. I have also realized that adjusting the flywheel speed or hood angle to counter this problem would alter the ball’s time in the air, which would mess up my earlier calculations.

Does anyone have suggestions as to how I should alter my approach? I am trying to avoid getting too deep into the physics of the ball (launch speed based on flywheel speed and friction, Magnus effect, etc) as I worry any approach I could take would be oversimplified and inaccurate. But if the only way forward is to investigate this approach, that would be useful to know as well.

Any advice and help would be appreciated! Thank you : )

8 Likes

I found this older post a while ago about shooting while moving that got me started off in the right direction. Basically instead of treating the robot as moving and goal as stationary, treat the robot as stationary and the goal as moving. Then you can find where the goal will be after the time it takes for the ball to get there and adjust shooter speed, turret angle, and hood angle from there.

You shouldn’t need to take into account magnus effect and all the physics stuff because it should be automatically accounted for in the data points you get from testing.

I haven’t been able to test my math yet (waiting for our robot to be finished), but if it works, I plan to make a post/guide about it after the season once I have more time.

10 Likes

Also if you’re curious about what an implementation might look like, here’s the relevant (untested) code that I have so far

I tried to explain everything with comments, but feel free to ask any questions. Note that I might not even need all the prediction stuff in there if the values aren’t changing much in between each loop, but again I haven’t been able to test yet so I can’t say.

I’ve messed around with the algorithm a lot, and I think I’ve got something that works. I do essentially what I detailed in the diagram (except I realized I was doing the transformation backwards), and the new distance to the goal (to determine flywheel speed and hood angle) is b. I have attached my code, which also contains a basic physics simulation to test the algorithm. I haven’t gotten to test much on a real robot yet either, but if I do I’ll provide an update. Also if anyone wants me to do a more in-depth explanation of my code I can provide that as well. mvsht.c (7.4 KB)

I’ll never turn down additional insight and explanation.
My team has other items we need to address first before making optimizations like shooting on the move, but having resources like this to reference in the future is what make CD such a great resource. Case in point: how many swerve drive code bases use the kinematic equations from Ether’s old PowerPoint and associated CD thread?
Having a trail blazed lowers the bar to entry significantly. It’s much easier to organize your thoughts and begin an implementation when you’ve been shown a way that problem can be solved.

If you’re willing to lay out what you’ve learned, there’s no doubt it will be put to good use.

1 Like

I spent a lot of time on the pain that is mathematically modeling a turreted robot shooter last year and I can share those calculations in the hopes that it may find you some use. I guess the “arc shot” math might be of use as well as the robot position stuff. In my opinion just finding some heuristic function to relate the two or some linear equation relating all your values with a table would be much easier to tune / troubleshoot, but if you are also a team w/o a big practice field to tune these things then I totally get where you are coming from.

    /**
 * Calculates the x y and z velocities required to hit the
 * specified target. Supports two firing modes, perpendicular and normal.
 * ---------------------------------------------------------------------
 * Perpendicular shot means that the ball will be calculated to come into contact with
 * the target at a 90-degree angle. This provides a better range than our dynamic shooting mode,
 * which is locked to its physical velocity limitations.
 * ----------------------------------------------------------------------
 * The dynamic shooting mode provides an accurate shot at a set velocity, meaning we
 * can achieve a much cleaner shot by picking the speed at which our turret is most accurate.
 * Using these two equations:
 * Straight Shot: sqrt(-4*g*z + 2*v^2 - 2*sqrt(-4*g^2*x^2 - 4*g^2*y^2 - 4*g*v^2*z + v^4))/(2*g)
 * Arc Shot: sqrt(2)*sqrt(-2*g*z + v^2 + sqrt(-4*g^2*x^2 - 4*g^2*y^2 - 4*g*v^2*z + v^4))/(2*g)
 * We solve for time, and translate that into a velocity which then gets subtracted from our robots
 * moving velocity.
 * This provides us with an accurate shot given any distance with our velocity, given it is within
 * the physical range of our robot.
 * It also allows us to have a much stronger PID on the flywheel, tuning it for only achieving a specific speed
 * in the quickest way possible.
 * ----------------------------------------------------------------------
 * This method is also responsible for tuning the auto aim, with our 3D graph script taking data from this method
 */
public void calculateVelocities(boolean perpendicularShot) {
    double x = getX();
    double y = getY();
    double z = getZ();
    double xVel = driveTrain.getYVelocity();
    double yVel = driveTrain.getXVelocity();

    double[] tempXYZ = {x / ShooterConstants.getTConstant() - xVel, y / ShooterConstants.getTConstant() - yVel, ShooterConstants.getZVelocityConstant()};
    if (!perpendicularShot) {
        tab.setEntry("Trajectory: ", "Curve");
        tab.setEntry("ED", extraDistance);
        double a = -4 * g * g * x * x - 4 * g * g * y * y - 4 * g * velocity * velocity * z + (velocity * velocity * velocity * velocity);
        double tStraight = Math.sqrt(-4 * g * z + 2 * (velocity * velocity) - 2 * Math.sqrt(a)) / (2 * g);
        double tArc = 1.41421 * Math.sqrt(-2 * g * z + velocity * velocity + Math.sqrt(a)) / (2 * g);
        double t = Double.isNaN(tStraight) ? tArc : tStraight;
        if (!Double.isNaN(t)) {
            graphTab.setEntry("T", t);
        } else {
            graphTab.setEntry("T", ShooterConstants.getTConstant());
        }
        if (Double.isNaN(tStraight)) {
            tab.setEntry("Trajectory: ", "ARC");
        }
        vXYZ = !Double.isNaN(t) ? new double[]{x / t - xVel, y / t - yVel, z / t + g * t} : tempXYZ;
    } else {
        tab.setEntry("Trajectory: ", "PERP");
        graphTab.setEntry("T", ShooterConstants.getTConstant());
        vXYZ = tempXYZ;
    }


this is what a “straight” shot vs an “arc” shot would look like. I kept velocity constant in this situation because our shooter last year really wasn’t accurate at any velocity other than one. The only velocity adjustments I do on the shooter are for interpolating for movement. If you want more details on the math lemme know and I can go into more details. I think my one big takeaway from this whole project was figuring out ways to accurately read the velocity of a ball coming out of our shooter. Ultimately everything had to be extremely precise to achieve reasonable results and it took us quite some time to finally dial everything in - if using physics to model your shooter is how you want to do it I wish you the best of luck.

4 Likes

What is x, y, and z? Is this the position of the robot relative to the goal? What is vXYZ and how does it get converted into an ultimate flywheel velocity?

1 Like

Maybe this doesn’t make sense, but could you just focus on cancelling the velocity of the robot with the shot? So if you are driving towards the goal, change the shot angle and velocity until the shot ball (*after it leaves the robot) will exactly match what it would for a stationary robot? If you make some assumptions about the theta direction, it simplifies quite a bit too, to the point where you are just changing your angle relative to the goal, not the distance/angle of the shot

To explain it a different way. We all know how to solve the static case, which tells you exactly what the x, y, and z velocities of the ball should be to hit the target perfectly. So all you have to do is change the horizontal angle, vertical angle, and velocity of the shot so that the velocity of the ball after it leaves the robot will exactly match the velocities of the static case

2 Likes

Our code from last year was completely posted online so feel free to take a look here:

x and y can be whatever you’d like them to be in the field-axis and z is always going to be the goal height - shooter height. x,y, and z should be units of meters.

vXYZ is just the velocity along those axis’s, in units of m/s. In our code we ended up making a table to convert an exit velocity in m/s into a flywheel speed in ticks/100ms and this to set it:

        // Returns ball's velocity in m/s
        double ballVel = Math.sqrt(vXYZ[0] * vXYZ[0] + vXYZ[1] * vXYZ[1] + vXYZ[2] * vXYZ[2]);

        tab.setEntry("ballVel", ballVel);
        return ballVel;
    }

    public double getCalculatedHoodAngle() {
        // Returns the hood angle using the relationship between horizontal and vertical velocities
        double hoodAngle = Math.toDegrees(Math.atan2(vXYZ[2], Math.sqrt(vXYZ[0] * vXYZ[0] + vXYZ[1] * vXYZ[1])));
        if (Math.abs(driveTrain.getYVelocity()) > 0.1) {
            hoodAngle += (driveTrain.getYVelocity());
        }
        tab.setEntry("Hood Angle", hoodAngle);

        if (getX() - extraDistance > 2) {
            hoodAngle += (getX() * getX()) * 0.2;
        }

        tab.setEntry("Hood Adjust", hoodAng);

        return hoodAngle + hoodAng;
    }

    public double getAngleToTarget() {
        //Returns angle to target using x y z position of target.
        double turretAngle = Math.toDegrees(Math.atan2(vXYZ[1], vXYZ[0]));
        tab.setEntry("Turret Angle", turretAngle);
        return turretAngle;
    }
1 Like

also one final note:
if (getX() - extraDistance > 2) { hoodAngle += (getX() * getX()) * 0.2; }

this might seem confusing but it was our method to account for air resistance especially when shooting from really far away

1 Like

I think I did the algebra for shooting while moving. The idea is to start with the parameters for a static shot, and to change the robot parameters (velocity, hood angle, turret angle) until they cancel out the motion. Or, to put it another way, the combination of the motion of the robot and the shot/applied motion of the ball equal the static shot parameters

I switched between coordinate systems a little. Lets define our output variables (sort of in spherical coordinates): v_s, theta_v, and theta_h. v_s is the shot velocity, theta_v is the vertical angle of the shot from the horizontal (hood angle), and theta_h is the horizontal angle on the field.

I described the robot velocities as v_x and v_y, the velocities in the x and y directions (cartesian, same reference point as theta_h is defined from)
I described the static shot parameters as V, phi_v, and phi_h. V is the velocity of the shot, phi_v is the angle from the horizontal, and phi_h is the horizontal angle (pointing directly towards the goal). These would be determined by a lookup table, or a trajectory calculator/optimizer. Assume these are known constants (for one shot)

The nest step is to convert everything to cartesian coordinates of the field; x, y and z. Then, we need to calculate the values of our output variables vs, theta_v, and theta_h, such that the combination of the robot velocities relative to the field and the shot velocity relative to the robot match the static shot values (V, phi_v, and phi_h).

This is set up as v_s_x - v_x = V_x, v_s_y - v_y = V_y, and v_s_z = V_z (because v_z is zero).

Using the conversions from spherical to cartesian coordinates, we can define v_s_z in terms of v_s, theta_v, and theta_h (v_s_x = v_s*cos(theta_v)*cos(theta_h), and so on for the other variables. Then we solve for the output variables (the solution is not terrible by hand, see the images below)

I haven’t coded it up yet or put it into a physics sim, I am somewhat confident it will work (and that I didn’t make any mistakes)


3 Likes

For me, Y is toward the goal, X is perpendicular to Y, and Z is the vertical axis

For what it’s worth, we found that the math was the easier part of shoot-on-the-move vs. actually getting all the error stackup (between computer vision, odometry, various sources of latency, control error, and the shooter itself) small enough that the ball still goes in the goal.

10 Likes

Right.
And we found out, that all this is still easy… compared to doing it during or right after fighting with a swerve strong defense…

6 Likes

I think I took a similar approach to what @jacob6838 was suggesting and canceled out the robot’s velocity instead of trying to account for the ball’s velocity as I described in my initial post.

In regard to @Jared_Russell and @kornblau, I have been considering making a separate thread asking for advice on how to supplement odometry (with vision or the like) to make it more accurate as odometry breaks down pretty quickly during teleop.

So it works in my sim…
shooting_while_moving_p1
shooting_while_moving_p2

13 Likes

Vey impressive? What language or graphic framework did you write your simulator in?

It is written in pygame, it was surprisingly easy

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