What are people doing to control their motors? Specifically, those dealing with arms and positions (not drive).
What I have going is a set-up that reads an array of structures containg pointers to the PWM values & input variables, analog in for the pot, and all the constants needed. Just before the call to putdata(), this is called to handle all the inputs. This same code is used for both autonomous and user modes.
There is also a debugging advantage to this: If part of arm doesn’t work, I can point to another part and say, “That works; the code is fine,” because it’s the same code with different values.
Our team created a large abstraction library (user_io.c/h,user_motors.c,user_controls.c) which basically has stuff like get/set_motor_speed and get/set_motor_pos and update_input/output(), which work on all motors. The arm routes though PID (position) and velocity, but others just simply go directly to the motors (with limiting, overflow and deadband checks).
In come cases, you can use position PID with the motors to make it line up correctly. My team was planning on using PID for the arm and motors (we had it working perfectly) until they made a last-minute decision not to attach or calibrate pots.
Although we had a large abstraction library, we could not use most of it due to a robot lacking some small parts. The motors library got too complicated, and I wasted an hour of time due to some default deadband value of 127 in the middle of the arm code.
Basically, either KISS (Keep it simple) or else heavily test every part of it, make easy-to-change numbers, and have #ifdefs to disable code when, for example, you are suddenly without pots.
One thing that helped a lot was the reuse of code (just call update_inputs() at the beginning of the auton and main loop, and update_outputs() at the end of the loops, and share arm and drive code: an arm motor is a motor and a drive motor is a motor, and drive can have position, and arm can have velocity.) If a value changes at last minute, then just edit it in one place, and not everywhere you use the motor.
For things like arm grabbers, pneumatics, etc., just make a simple flag in a controls structure which reads from the control board or the auton loop. Then just use a timer and set a relay to 1 or 0 or a motor to forward or backward based on that timer.
That’s pretty close to what we use for our arm and joint.
We have a tcr_joint struct that takes in:
pointer to pwm output, pointer to joystick axis, pot ana_in, current pos, servo gain, servo flag, min pot position, max pot position, pot counts per degree travel numerator, pot counts per degree travel denominator, pot position for zero degrees.
We have the counts per degree (integer math as well) so we can set degrees using our scripting interface. The min and max are limits before the hard physical stop.
We actually servo to position, being called after the joyaxes are updated but before put data. We have both a gas shock on our arm and the globe motor while the slider has dual windows (no backdrive).
This allows us to set a position in degrees and once there, the motor will keep the arm at that degree measure, not falling when a tetra is placed or otherwise.
Our PID started off in OOP fashion, with arrays of pointers to structs representing the motor subsystems, and pointer to functions representing class member functions. It soon fell apart when we started getting memory corruption (garbled printf, Code Error light)
In the end, I reverted back to traditional, procedural C with minimal functions in heavy processing routines (but auton still remains very modular!)
I rest my case that abstraction is still “not there yet” for micro programming, especially with the lack of a properly optimizing compiler (not saying MCC is a bad compiler – just not ‘tough enough’ to handle complex C).