Four omniwheel drivetrain, driving in pure X or Y movements
Front and back wheels (for X movements) are independently controlled
Left and right wheels (for Y movements) are controlled together (to save PWM pins)
Servo library deactivates PWM functionality on two pins
If we can have encoders we’d like that so we can have closed loop position control
Gotta go fast! (filling the pot by driving 6 balls at a time requires ~2sec time across field)
Be stronk: Hitting walls stops the robot faster than wheel traction, and aligns the robot
The motors available at the lab are kind of weak.
Oooh look AndyMark is selling $22 RS-775 series motors for just $10 and they have encoders and output about 40W of mechanical power. Let’s get four of them!
The motors don’t have gearboxes, but we can make gears for free* so it makes sense to spend our budget on powerful motors with encoders even if they don’t have gearboxes.
The motors have 11 tooth 0.6 module pinion gears. If using a single stage reduction, the largest gear that easily fits within the diameter of the 3.25in omniwheels available at the lab is 120t.
Larger gears that require larger wheels don’t really change the speed or pushing force of the drivetrain and just take more space so we used the smaller wheels.
So gear ratio is 11:120
This isn’t the ideal ratio other than that it fits mechanical constraints but it’s ok
At maximum rated motor power, driving speed is 3.7ft/sec and pushing force is 3.5lbs
Also simulated drivetrain with AMB calculator to check calculations
~2sec time to cross the field is possible (and later proven in testing)
Also we were running our motors at the battery voltage, which was ~1.5x higher than the 12v Andymark specifies their motors at.
Motors have 28 pulse per revolution quadrature encoders, so encoder resolution is 0.85mm in theory. This was found to be almost exactly 1/31 inches in practice and we programmed our code in 1/31 inches.
Goal was to have strong, easily maintainable/swappable modules that are all identical.
We tried laser cutting aluminum gears at lab64, but the edge was too rough, even when cutting gears from thinner sheet and stacking them. Waterjet cutting the gears worked well enough (thanks to Ahmad, Elena, and Olivia at the PRL) though we had to cut slowly and this meant we had to pay for $15 worth of abrasive. This still cost less than buying gearmotors with encoders in the power range of our motors, and it was fun to use the waterjet.
We cut the gears and drive module parts from ¼” aluminum because the stock was free
Drive modules are patterned rotationally so no mirrored versions
Wheels are on shoulder screws and easy to replace
Gear tooth strength calculation
We needed to check that our aluminum gears are strong enough as well as that the pinion is strong enough for our application
The pinion is designed for use in a planetary gearbox where it is driving three planet gears, not one spur gear, so it might not be strong enough.
Motor stall torque is 35oz/in, pinion’s pitch radius is 0.13in, so transmitted load at tooth is 16.8lb
Steel pinion has max calculated load of 26.71lbs
Aluminum waterjet gear has max calculated load of 39.5lbs
So it should be strong enough! (and it did work in real life!)
For any X or Y movement:
Driven wheels have 50% of weight, in the middle of the wheelbase
Center of mass is about 30mm above the wheels’ axles
Wheelbase is 230mm
Wheel CoF is 1.1 (we cleaned the wheels with alcohol wipes between matches)
So traction limited acceleration is about 0.43g (this would become motor limited at around 3ft/sec)
Let’s divide this by 3 just to be sure our wheels don’t slip. So acceleration is software limited to 0.16g or 1900 encoder ticks / s^2 (more on control loops later)
Using encoders means the robot only knows where it is if wheels don’t slip
[these aren’t very interesting as they just send analog write to the PWM pin and digital write to the direction pin]
Front
Back
Y (left and right)
Spin
Stop
Our code uses the Derivs_Limiter Arduino library to limit the first and second derivative of a target position variable as it moves toward our position goal during a move. By limiting our acceleration, we prevent wheel slip, and avoid blowing the fuse. On the field we never actually reach the velocity limit, so Fryborg is always speeding up or slowing down while it drives.
Fryborg uses a Byte-Sized Encoder Decoder co-processor for reading the four quadrature encoder signals and sending them to the main arduino over I2C.
Fryborg’s drive code uses a proportional control loop using feedback from the encoders. The resolution of the encoders is coarse enough (and the Derivs_Limiter position target changes gradually enough) that we found simple proportional control worked well. Adding full PID didn’t improve our robot’s performance and just used more Arduino memory.
There are three control loops, for the front motor, back motor, and pair of side motors. The side motors use the left encoder for feedback as the right motor was dead for a lot of our testing. Adding an average of the left and right encoders didn’t improve performance. The separate control of the front and back motors keep the robot from accidentally rotating.
Our first function for moving to a specified position (AccelPosition) returns true when the encoders have measured that the motors have moved the specified amount. This works well for movements on an open field, where the robot is expected to reach its position goal.
However, in the majority of cases, Fryborg actually hits a wall at the end of its move, so we use a different function WallAccelPosition that returns true when Derivs_Limiter has finished moving the target position to the goal and the measured velocity of all four encoders is zero.
The functions allow for diagonal moves but they don’t coordinate the movement in the X and Y axes and we don’t do any diagonal moves.