Yes, we will be releasing the code for this (both path generation and follower controller) later this year. Right now we are turning over student leadership and most mentors are busy barbecuing
I would like to elaborate on this system a bit though.
From the start of the season (Or rather, once we were set on a robot that carried 3 balls to a closer shooting position) we wanted the ability to drive smooth S curves. The reasoning was we knew we were going to need to get over to the one point goal to avoid defensive goalie robots by the time Einstein came around. In place of this system we could have used the typical “Drive, turn X degrees, Drive” auto driving architecture, but decided this was not going to work based on our troubles with translating and spinning in 2012 and 2011.
The implementation of this system was a tool [1] that allowed the programmer to specify a list of waypoints for the robot to drive. Each waypoint consisted of a X coordinate, a Y coordinate, and heading (Assume the robot starts at 0,0,0). From there, we found a set of splines that intersect each waypoint (at the desired heading) and fit within a maximum radius which was something that we knew we could drive (aka no abrupt 180* turns).
Next, we figured out what it would take to get the robot to drive that path.Given a maximum velocity, acceleration, and jerk (derivative of acceleration) for the robot to drive with, we walked the spline-y path generated above in steps of 0.01 seconds. For each step, we generated (Position, Velocity, Acceleration, Jerk, Heading, X, Y, and current time step) for the geometric center of the robot. These values corresponded to how the robot /should/ be moving at that point in time. From there, given the wheel base of the robot, we were able to build two sets of steps, one for each wheel. These lists of steps were built into arrays which we serialized to a text file.[2]
On the robot side we built a controller that would drive the robot on the path (after deserializing). The controller that made the robot move consisted of 3 sub controllers: 2 wheel controllers for the left and right wheel, and a heading controller that would correct the angle of the robot on the field.
The wheel controller which followed the trajectory basically looked like this (one running for each wheel, using the given wheel’s path data:
double update() {
Segment step = follower.getNextSegment();
double motor = 0;
motor = (step.vel * K_vel) + (step.acc * K_acc) + (step.jerk * K_jerk);
motor += ((step.pos - getWheelPos()) * K_p);
return motor;
}
The heading controller looked something like this
double updateHeadingController() {
Segment step = leftWheelFollower.getCurrentSegment()
return (step.heading - gyro.getAngle()) * K_heading;
}
And the overall system looked something like this
updateDrive() {
double l = leftWheelController.update();
double r = rightWheelController.update();
double turn = headingController.update();
leftMotor.set(l+t);
rightMotor.set(r-t);
}
So basically, even without a gyro or encoders we could drive something like the path just by pressing ‘play’. Once the closed loop position and heading controllers were turned on, it was much better.[3]
We will release this code over the summer. Hopefully it will make more sense then.
[1] This tool was run on a laptop, as the math to build all the paths was pretty slow on the cRIO.
[2] We originally wanted to render java class files with static arrays defined for each path, but we ran into an issue with the tool that packages the final jar file for deploying. It could not handle more than about 1KB of static data in any Java class.
[3] We did no work to validate that our paths did not cause motor saturation. Whenever the driving looked weird we would down the velocity or move the waypoints to make bigger curves.