MKHLib has a powerful CatapultIntakeController
class that handles control of the intake and catapult. The two are merged together to safeguard against discs being intaken under a recovering catapult.
The CatapultIntakeController
has functions for shooting with limit switch stopping, and waiting for different states in autonomous.
The gear ratio for intake to roller is given in the constructor, so that speeds and positions can be passed relative to roller and intake. The roller can be controller by PID or by time.
MKHLib implements 2 new custom driver control schemes:
Given
The above equations elegantly combine the forward and backward motion of the
So, given
Note how the ratio
The ratio
This ratio will stay the same if the stick values are both multiplied by the same number, as multiplying every term in both the bottom and top of a fraction by the same number is like multiplying the fraction by
For instance suppose we double
Note how both motor powers double, and cancel out to the same ratio
So the ratio should grow consistently as long as relative magnitudes of inputs are the same, but this math breaks down at higher speeds.
Let's double the previously doubled stick values once more.
This is fine and dandy. However, how can 120% power be applied to the left motor? It can't. In the classical arcade skid drive
This is a less aggressive turn (as
Instead of rounding when
So
The normalized Arcade retains the classic arcade feel while still performing well on turns at high speeds.
The MKHLib normalized arcade is built on top of EZ-Template. This means you get your input curving and active brake, just with an alternate control method. Simply call the new Drive.arcade_normalized_standard(ez::e_type stick_type)
and Drive.arcade_normalized_reversed(ez::e_type stick_type)
functions just as you would use any other Ez Template drive function in opcontrol()
.
(Curvature throttle interp)
The ratio
The formulae
With the above equations the ratio
In both versions, the simplified ratio
This means super high performance and intuitive control on turns at high speeds.
What about point turning?
Generally when
Most implementations fix this by swapping to tank controls sharply (by way of a deadzone) under very low speeds (of the orders of
So curvature drive performs well at high speeds, but bad at low ones, and a hard deadzone switch feels unnatural.
Normal tank drive feels great at low speeds, but bad at high ones.
The curvatherp algorithm smoothly linearly interpolates between tank drive for the slow speeds, and curvature for the high speeds. This means the point turns and tight low speed performance of tank drive, and the intuitive high speed turning of curvature, without the clunky discrete switch.
The interpolation is done simply with linear weighting:
,
Let
Power
Boilerplate linear weighting. The issue is finding
The user inputs 2 values
(
Substituting:
2 unknowns, 2 equations. First solve for
Now solve for
Plug back into original defenition of
If the maths proof isn't enough, it can be explained intuitively:
First start with
Now we must make the equation rise to
Again the curvatherp is built on top of Ez Template. Unlike normalized arcade, Drive::arcade_curvatherp_standard(e_type stick_type, double interpolator_start, double interpolator_end)
and Drive::arcade_reversed_standard(e_type stick_type, double interpolator_start, double interpolator_end)
need 2 special parameters for the start and end of the interpolation. These parameters are between 0 and 127, as the max value of the controller stick is 127 in practice.
Odometry is implemented directly into the Ez Template chassis
. Simply pass a width into the constructor and odometry is handled using the same IMU and encoders for PID.
MKHLib implements a simple function to turn to a point. It simply acts as a proxy to the normal Ez Template turn PID and sets the target value based on the angle to the point. That means that if the robot is pushed after the turn to point has been called, it wont adjust and still turn to that point; the PID only accesses the odometry position at the beginning of the motion, hence the term motion planning. MKHLib isn't running a PID on any of the odometry data, it's simply using it to inform the normal Ez Template PIDs.
The dot product is the multiplication of two vectors that yields a scalar.
There are two main ways to calculate the dot product of two vectors:
In cartesian coordinates:
This is a simple multiplication and addition of both
Yielding an identical result, in polar coordinates:
This involves two operations: first you project one vector onto the other, then you multiply their magnitudes. The cosin between two angles is the projection, it gives the adjacent component of a vector to an angle. This is illustrated in this image from mathisfun.com:
MKHLib doesn't mess with complicated driving algorithms. To simply drive to a point using motion planning MKHLib does two things: turns to the point, then drive as close to it as it can in a straight line. So how does MKHLib figure out what distance it needs to drive to get as close as possible?
Consider two vectors: the unit vector representing the direction the robot is facing
The distance to drive
While originally there was no intention to do so, iterative motion has been added to MKHLib. The addition of this reduced the time of the skills autonomous by 10 seconds.
There is iterative point turning, to facilitate more accurate auto aim.
Point driving is simple to do, but hard to make stable. In theory its just combining two of the previous functions talked about and running them iteratively. By driving straight to a point, and constantly turning to face it as accurately as possible, you can achieve motion to that point.
However, iteratively turning to a point becomes increasingly unstable as the translational error
So the trick is to split the motion up into phases, and simply turn off the angular component of movement at a certain distance. It works as follows:
PHASE 0, error > 12: iteratively update straight drive target and point turn target using math described above.
PHASE 1, error > 9: iteratively update straight drive target.
PHASE 2: stop updating all targets, let PIDs run their course.
Note: all drive PIDs run on encoders, and the distance target is converted into an encoder target. Because of this the user just has to tune one PID for all driving, odom based or encoder based.
The classical drive functions give a good fast path to reach a target position, but they don't control for orientation. The stupid way to do that is simply to turn after the drive is done. The issue is that this doesn't look smooth, and in some cases isn't fast. The alternative is driving in a curve, and in some cases this curve is faster, because when driving in curves you spend less time accelerating. One way to generate these curves on the fly with nothing more than creative iteration of the point driving that has already been tuned is the boomerang controller.
Given a target point
The distance of the carrot point from
The final formula for the carrot point is:
With this you can get semi-accurate control of orientation in drive motions. The boomerang controller isn't perfectly accurate, and should not be used for applications where orientation needs to be precise, such as aiming a shot. For that a point turn should be done after the motion.
Much in the vain of the boomerang controller, path following is done through virtual points.
Pure pursuit is simply done by picking the farthest point on a path up to a certain distance and following it.
In the classical pure pursuit, the robot is set to drive along a curve tangent to the current heading and intersecting the target point. Sparing the mathematical specifics: there is one main problem with implementing this. To Accurately drive along a curve, you have 3 options: built in motor PIDs, complicated motion profiles, and custom motor velocity PIDs. Motor PIDs for a velocity are much harder to write and tune than those for position, motion profiles are an unabstracted and untunable mess, and built in motor PIDs are even more unoptimal than the other two.
THe MKHLib solution to this is simple: use something tunable and intuitive and add onto it with the good parts of pure pursuit. The idea of a lookahead to find a point on a path (thereby dynamically generating curves on the fly) is really cool, and tends to work very well for somewhat smooth motion on a path. The idea of driving throgh a curvature however is not, because of reasons mentioned earlier. So the MKHLib Pure Pursuit is simply just an extension of the previously mentioned Point Driving, just iteratively updating with a new target point a-la pure pursuit.
This means that the same PIDs tuned for point driving and point heading can be used for following paths, with almost perfect stability!
I love PIDs. They are simple and they work. The idea with MKHLib originally was actually just to be a wrapper for Ez Template that calls the Ez Template functions iteratively based on odometry. Eventually it became more feasable to integrate it into Ez Template but the idea of iterative calls of straight line PIDs to drive in 2D space has been kept around since then. The reason for this is that tuning in 1D space is way easier than tuning in 2D space, and having your 2D functions just be 1D functions with some sauce means you never need to do that tricky 2D tuning. To tune all of my driving all I had to do was tune two straight drive PIDs, a turn PID, and a heading PID.
In reality all points that you drive to, all paths that you follow, are abstract. The values you input are not real field inches. They're close to positions on the field, but they have variance. This is acceptable. You don't need a perfectly accurate coordinate system that works just like reality. You just need it to be consistent. It's importatn to recall that everything is virtual when writing libraries. Some programmers use a tool to draw paths on an image of a field. I don't. A path drawer is too idealized, it exists in a utopian reality where your robot's real driving path is accurate to some value you can enter on your browser. Rather than banging your head against this with endless tuning it's better to let the chaos into your life and accept it and the numerical tuning that comes with it.