After researching a variety of motors, we decided on the Nidec 24H BLDC motors for actuation due to their low minimum angular velocity and how well they respond to changes in direction, both of which are crucial for precise control near the equilibrium of the stick.
The rest of the hardware consisted of the stick, the homogeneous mounting plate for both motors, and the two reaction wheels, we utilized CAD to model them and then 3D printed them with 15% infill PLA (making for an average density of ~0.7 g/cm3). Our first iteration had several design flaws:
The center of gravity wasn't at the center of the system, so we repositioned the stick to be at the center of gravity;
We increased the size of the wheels in order to increase the moment of inertia and maximize the generated torque.
Given that our system is modular, we went through multiple iterations of each individual part in order to address these issues. In our final model, we also added weights to slots of the reaction wheels to manipulate the system's moment of inertia, which directly increases the torque generated by each reaction wheel.
CAD initial iteration, rear view
CAD final iteration, rear view
CAD initial iteration, front view
CAD final iteration, front view
3D Printing the motor housing
Printed parts of initial iteration
Configuration of final iteration
Initial scheme for bidirectional motor control with BLDCs and ESCs
Initially, we designed and implemented H bridges to allow the motors to run both clockwise and counterclockwise based on IMU readings. We then hooked up the H bridge circuits to a relay configuration to control the motors using software. Later, we positioned the batteries and implemented the reaction wheels such that they would intrinsically have bidirectional capabilities without the H bridges.
An early prototype of the complete system, with an IMU and two bidirectional reaction wheels
MPU 6050
Together with an MPU6050 inertial measurement unit (IMU) attached near the bottom of the stick, the motors send and receive data through a wired connection to and from an Arduino Uno that sits apart from the stick. Powering the motors is a 11.1V LiPo battery that also sits separately, while a 5V usb connection to a computer powers the Arduino. We optimized our wire management throughout our implementation to ensure maximum organization and data transfer between electrical components.
Performing polarity tests
All of the code was written in C++, which is readily supported by Arduino. The code repository can be found here: https://github.com/jeiook/106a-leapfrog. This schematic details our software implementation.
Accurate IMU readings are crucial since they determine our perception of the state of the stick. Dry runs with just the IMU revealed that the readings have a constant bias. In other words, at equilibrium, the angular displacement readings would be non-zero for at least one axis. In order to account for this, the setup part of the code includes a calibration step which zeros out the bias for each axis with an appropriate offset, which is determined by collecting samples of the IMU readings and then filtering them with an exponential moving average. Reading from the IMU during normal operation is instead achieved through a Kalman filter. Both were chosen based on experimental results.
The routines of each of the motors and the IMU are encapsulated in the motor controller class and IMU class respectively, freeing the code base from redundant code and enabling any future addition or swap of reaction wheels and IMUs (if need be) to be achieved with around three lines of code (instantiation, setup, and reading/writing in the main loop). The PID parameters Kp, Kd, and Ki, and another term proportional to the motor velocity (Kv) are instance variables of the motor controller class, so that the parameters for each motor can be tuned separately.
Sofware Schematic
Code to calculate angular displacement and velocity
Code for the exponential moving average filter
The complete system behaves as follows:
The software calibrates IMU offset;
Each of the motors spin to indicate they are in a working state.
Then the system goes into a loop with these steps:
IMU readings get sent to the Arduino to get filtered;
The code calculates angular displacement, angular velocity, and the integral, until the current time step, of the angular displacement of the stick using IMU data;
The code calculate the control term for each motor based on the above quantities, the motor angular velocity (discrete-time derivative of the encoder count), and the K parameters (Kp, Kd, Ki, Kv);
The code converts the control term to a voltage output to each motor and apply it to both motors;
The stick rotates about the pivot in response to the reaction wheel torque.
Simplified Control Diagram
Complete system in action