The moteus brushless controller can drive many motors out of the box, but until now it has been challenging to use with gimbal style brushless motors. They are wound with thin wire so that they have a very high winding resistance, and thus can be driven by inexpensive low current controllers. Using something like moteus with a gimbal motor isn’t absolutely necessary, but does give benefits in terms of high performance trajectory tracking and torque control.
The primary challenge when using moteus with high winding resistance motors is the mechanism moteus uses to sense current through the motor phases. It has a set of 3 current sense shunts, basically resistors, that have a small voltage induced across them proportional to the amount of current flowing. When commanding a specific current, moteus continually adjusts the voltage on each of the phase windings until the desired current is achieved. These resistors are sized so that moteus has reasonable resolution up to its 100A maximum current rating. When used with a motor that has a maximum current of 1A though, almost all of this resolution is wasted.
In the past I have replaced the sense resistors on moteus boards in order to drive gimbal motors. That is the optimal solution, as it allows moteus to continue to sense the current through the windings and provide accurate torque control. But not everyone is set up to perform surface mount soldering, and accurate torque control at all speeds and temperatures is not required in many applications.
Voltage mode control
As of release 2021-12-03, moteus now has a new configurable option, called “voltage_mode_control”. In this mode, torque commands no longer use the current sense resistors for feedback. Instead, they use the calibrated phase resistance of the motor to derive what voltage would be necessary to achieve a given torque.
The downsides of this approach are that there are many factors that will make the mechanical output torque not match what was intended. For instance, the back EMF if the motor is moving at non-zero velocity, changes in the winding resistance due to temperature, or inductive coupling between the quadrature frames at non-zero velocity. All of these factors will reduce the accuracy of the torque command, so that the mechanical torque output will not match what was commanded. However, for many applications, the motor is not spinning quickly and precise torque control is not required.
Since this control mode takes effect only at the end of the normal moteus control loop, it means that all of the “position” mode control works as before. Thus the constant velocity mode, stop position, and the “within” mode can all be used without regard to whether the controlled motor is running in the normal “current mode control” mode, or in the new “voltage mode control”.
With the new moteus_tool calibration mechanism, this means that gimbal motors can be used in a basic fashion out of the box with moteus by tweaking only a single configurable value: servo.voltage_control_mode. Here’s a video showing how that works with a real motor.
To date, I’ve used the moteus controllers exclusively for joints in dynamic quadrupedal robots. However, they are a relatively general purpose controller when you need something that is compact with an integrated magnetic encoder. For the v3 of my Mech Warfare turret I’m using the moteus controllers in a slightly new configuration, with a gimbal motor, one for each of the pitch and yaw axes.
Gimbal motor theory and current sensing
From an electrical perspective, gimbal motors are not that all that different from regularly wound brushless outrunners. The primary difference being that they are wound with a much higher winding resistance. That enables them to be driven with a much lower current, at the expense of a lower maximum angular velocity. In this case, I’m using the GM3506 from iFlight which has a winding resistance of 6 ohms, that results in working currents being on the order of 2A maximum.
The moteus controllers are designed to drive motors with phase currents in the 20-60A range. To operate in current control mode, they use a current shunt resistor connected to a dedicated amplifier for each phase. The current sensing resistor determines the range of currents that can be accurately sensed. The resistor that the controllers have by default measures 0.5 milliohms, which gives a reasonable tradeoff between accuracy and power dissipation for 60A operation. However, for 2A operation, it results in pretty low resolution current feedback. Thus for this application, I substituted in 5 milliohm current shunts:
Control modes and noisy velocity
The other potential challenge, is that the velocity signal that can be derived from the AS5047 absolute magnetic encoder in the moteus controller is relatively noisy when operated with no gearbox reduction. That limits how much derivative gain can be used in a position control loop. You could get around that by incorporating more plant knowledge in a filter or state observer, but that shouldn’t be necessary here.
Fortunately for this application, I won’t be controlling position based on the absolute magnetic encoder, but instead based on the inertial measurement unit in the primary controller. The absolute magnetic encoder will be used solely for performing the DQ transform to implement torque control, for which the noise in velocity is irrelevant.
Next up is making these controllers actuate the turret.
Well, that took longer than I expected! I last showed some progress on a gimbal stabilized turret for Mech Warfare competitions more than six months ago. Due to some unexpected technical difficulties, it took much longer to complete than I had hoped, but the (close to) end result is here!
Here’s a quick feature list:
2 axis control: Yaw and pitch are independently actuated.
Brushless: Each axis is driven by a brushless gimbal motor for high bandwidth no-backlash stabilization.
Absolute encoders: Each axis has an absolute magnetic encoder so that accurate force control can be applied to each gimbal, even at zero speed.
Fire control: High current outputs for driving an AEG motor, an agitator motor, and a low current output for a targetting laser are present.
7v-12V input: Supports 2S-3S lipo power sources.
12V boost: When running from 2S lipo, it can boost the gimbal drive up to 12V for additional stabilization authority.
HerkuleX protocol: The primary control interface uses a native Dongbu HerkuleX protocol; support for other UART based protocols which will work at 3.3V CMOS levels should be easy.
USB debugging support: A USB port is present to return high rate debugging information and allow configuration and diagnostics to be performed.
BMI160: This IMU is used as the primary source of inertial compensation data. The board hardware supports a second IMU, to be placed on the main robot, but the firmware does not yet support that configuration.
This gimbal design contains three custom boards, a breakout board for the BMI160 IMU, a breakout board for the AS5048B magnetic encoder sensor, and the primary board which contains the rest of the logic.
BMI 160 Breakout
The first board is simple; it is a basically just a breakout board for the BMI160 inertial sensor. It provides the BMI160 itself, some decoupling capacitors, and a 0.1 inch 4 pin connector for the I2C bus.
I had these prototypes made at MacroFab which I highly recommend as a great provider of low-cost turnkey PCB assembly.
This, like the BMI160 breakout board, just has decoupling capacitors, the chip itself, and connectors. It additionally has mounting holes designed to fit onto the 3508 gimbal motor. This was printed at OSH Park and hand-assembled.
Gimbal control board
The primary gimbal control board contains most of the system functionality. It is designed to mechanically mount directly above the yaw gimbal motor, as the yaw absolute magnetic encoder is in the center on the underside of the board.
This prototype was also built at MacroFab, who did an excellent job with this much more complex assembly.
The connectors and features are as follows:
Power and Data: A 4 pin JST-XH connector in the upper right brings in power and data from the main robot.
Debug USB: A debugging protocol is available on this micro-USB port.
Camera USB: Two 4 pin JST-PH connectors provide a convenience path for the camera USB. The turret’s camera connects to the top connector, and the main robot connects to the side facing connector.
I2C peripherals: 3, 4 pin JST-ZH connectors have identical pinout and connect to external I2C peripherals. These are used for the primary IMU, the pitch absolute magnetic encoder, and the optional secondary IMU.
Arming switch: This switch is connected directly to the enable pin on the MC33926, and is also connected to an input on the STM32F411.
Programming connector: The 6 pin JST-PH connector has the same pinout as Benjamin Vedder’s VESC board, and can program and debug the STM32F411.
Weapon connector: A 2×4 0.1 inch pin header has power lines for the AEG drive, the agitator drive and the laser. It has an extra row of pins so that a blank can be used for indexing.
Gimbal connectors: 2, 3 pin 0.1 inch connectors power the yaw and pitch gimbal brushless motors.
The firmware was an experiment in writing modern C++11 code for the bare-metal STM32 platform. Each module interacts with others through std::function like callbacks, and the entire system is compiled both for the target, and the host so that unit tests are run. Dynamic memory allocation is this close to being disabled, but it was necessary for newlib’s floating point number formatting routines, which just allocate a chunk of memory the first time you use them. Otherwise, there is no dynamic memory used at all.
It relies on a CubeMX project template for this board. Most of the libraries CubeMX provides have too little flexilibity to be used for this application, so much of the bit twiddling is re-implemented in the gimbal firmware. CubeMX is great for configuring the clock tree and pin alternate functions however, especially in a complex project like this.
Both configuration and telemetry rely on a templated C++ visitor pattern to perform compile time reflection over mostly arbitrary C++ structures. Any module can register a structure to be used for persistent configuration. Those structures can be changed through the debugging protocol, and can be written to and read from flash at runtime. Each module can also register as many telemetry structures as necessary. These can be emitted over the debugging protocol either at fixed intervals, or whenever they update.
There are three possible modes, the first of which is what I call “open-loop”, and is based on the same principles as the BruGi brushless gimbal, where no absolute motor feedback is available. In that mode, a PID controller operates with the axis error as the input, and the output is the actual phase position of the BLDC controller. In this mode, the integral term does most of the work in stabilization, so the overall performance isn’t great.
The second mode still uses a PID controller, but now the output is an offset to the BLDC phase necessary to hold the current position as measured by the absolute encoders. This effectively makes the output a direct mapping to force applied to the motor, although of course a non-linear mapping. This mode results in much better overall performance and is easier to tune.
Finally, there is a third debugging mode that lets you just hard command specific BLDC phases. This is useful for calibrating the mapping between BLDC phase and absolute encoder phase.
The debugging protocol is partially human readable, but telemetry data is encoded in the same binary format as used elsewhere in the mjmech codebase. tview is the debugging application we use to read that data, as well as configure and control the overall system.
The bottom pane just has a serial console, where you can send arbitrary things over the virtual serial port. tview directly supports relatively few commands from the debugging protocol, and for instance has no UI to operate the stabilizer or fire control, so for now these are done by hand in that window.
The left pane has two tabs, one with a configuration tree and the other with a telemetry tree. The configuration tree shows all structures which were registered as configurable, and allows you to change them in the live system. The telemetry tree shows all structures registered as telemetry structures, and reports their values live as the system is operating.
The right pane has a live plot window where any of the values in the telemetry tree can be plotted versus time. It is just an embedded matplotlib plot, so all the normal plot interaction tools are available, plus some from mjmech’s tplot, like the ability to pan and zoom the left and right axes independently.
And last but not least, here is a short video demonstrating the turret stabilizing a camera and firing some blanks at a target as our mech walks around.