High-Current Motor Controller

Embedded Motor Control Applications using Raspberry Pi & Mbed
Project By: Adam Halverson (abh222) and Nishad Mathur (nm594)
Completed: 12.07.2017

Demonstration Video

Generic placeholder image

Project Objective:

The purpose of this project was to develop a multi-channel, high-current motor control system. In many robotic applications, the ultimate deliverable power of servo motors is limited by built-in H-Bridge circuitry, which is typically rated for low-current operation between 3.6-7.2V. This project's objective was to develop a substitute, closed-loop control system capable of supplying up to 30A/24V (0.72kW) per channel. These channels would draw power from a supply that is independent of the logic circuitry.


In pursuing the objective of this project, a Raspberry Pi was fitted with an external I/O device that was capable of multiple PWM outputs and analog inputs. External amplification circuitry based on N-channel MOSFETs was designed to interface with this device in order to reproduce the PWM control signals using the external motor power supply. To enable bi-directional control of the motors (without the use of a multi-polarity power source), a relay system was configured for each channel. This switches the polarity assignment to the motor terminals based on the embedded controller signal. To reduce hazardous back-EMF brought about by the high-inductance in the switching operation, flywheel diodes were fitted across the respective inductive loads in the external circuit. This was particularly important in protecting the I/O hardware from back-EMF induced voltage spikes.

The embedded system, which was programmed on the Raspberry Pi, used a myopic policy to adjust PWM-outputs and motor-direction signals. The control policy itself consisted of a simple exponential increase in PWM-output when analog feedback differed from target, saturating after a specified delta at 100%. The control also outputted a binary signal to switch polarity assignments when retracting the actuators. The Raspberry Pi communicated via serial to the external hardware I/O device in order to invoke these signals. The general system layout is given by (1).

Figure 1: High-Level System Flow Chart

The Raspberry Pi's touchscreen interface allowed for control of 4 motors through the adjustment of on-screen position bars, with each motor being able to be cycled on/off by clicking the status light above its respective feedback display (2). A stall-out routine was also programmed to protect the user/hardware; this temporarily disabled the motor in the event that maximum PWM-output did not result in movement of the motor. This is communicated by switching the status light for the respective motor to yellow. The user may attempt to move the motor again by simply adjusting the position assignment, or tapping status light.

Figure 2: Motor-Control System piTFT User-Interface

Design & Testing


The circuit diagram shown in (3) illustrates the external circuitry design that was generated to amplify the PWM signals, control the relay directional relays, and interface with the KL46Z. In order to amplify the PWM output signal from the microcontroller, high-current N-Channel MOSFETS were selected to act as amplifiers. When sufficient voltage is applied to the gate of a MOSFET, the doped region of the FET becomes saturated and allows current to flow through. In this case, it closes connection from the negative-terminal of the motors to ground, allowing a flow of power to the actuators from the motor power-supply.

Figure 3: External PWM-Amplification & Control Circuit Design [1]

Each linear actuator has a stall-current rating of (15A/30A) for a (12V/24V) power supply, respectively. [4] Thus, given a maximum design selection of 24V, each MOSFET must be capable of handling at least 30A of current. The selected MOSFETs (4) are capable of handling up to 60V and 30A of continuous load. [2]

Figure 4: N-Channel MOSFET (#FQP30N06L, 60V, 30A Continuous Load Rating) [2]

The gate voltage required to turn on/off (close the connection) of this MOSFET is 3-5V, with 10mA of required gate current. This is within range of the outputs of the KL46Z microcontroller. Thus, this selection satisfies the motor-drive criteria, and is shown in the circuit as Motor Drivers A through D.

An important aspect to consider with this design, however, is the back-EMF that will inevitably be experienced by the circuit as a result of inductance. This was a set-back encountered during the design and testing process, which was rippling back through the circuit and invoking a reset of the connected microcontroller. Back-EMF is produced when there is significant change in the current flow of a system with high-inductance, which creates a voltage-spike that can damage MOSFET circuitry. Thus, flywheel diodes needed to be added across both the MOSFET, and the DC motors, such that these voltage spikes could be rectified harmlessly when invoked.

Given this system is not being designed with a multi-polarity power supply, and bidirectional control of the DC motors is required, a switching circuit was needed in order to swap the port connections to the motors when necessary. This may be performed using a single-pole, double throw (SPDT) relay for each port, as given by (5).

Figure 5: Single-Pole, Double-Throw Relay (#JQX-15F, 30V, 30A Continuous Load Rating) [3]

Two of these devices will be required per actuator in order to swap both motor ports between the ground and positive input signals. Relay pairs will share the same control signal and swap simultaneously. In the event of a relay failure, the polarity will simply be the same on both ports, and as such, no hazard is presented. However, the required pick-up ratings for the input control signal is 5V, 185mA. [3] Given two of these will be driven by the same control signal, this exceeds the ratings of the microcontroller output. Thus, the relays drive signal will need to be powered using an additional MOSFET circuit, which will pull power externally to create the relay-driver signal (as shown in the electrical schematic).

To ensure that the amplification circuitry met the required switching speeds, the circuit was configured and tested at the maximum reasonable PWM-frequency (1kHz) while connected to an external DC motor. The results are shown in (6).

Figure 6: 24V PWM Drive Signal amplified from 3.3V PWM Control Signal

The circuitry displayed no significant propagation delays and matched the frequency with satisfactory precision. Thus, the full circuit was built and fully-configured to interface with all four motors, as shown in (7).

Figure 7: Fully-Integrated Control Circuit

Additional LEDs were added across the various MOSFETs for visual diagnostic purposes, with green LEDS reflecting the intensity of the PWM signal and red LEDs displaying the relay status for the respective channels.


The overall architecture of the system consists of two primary threads for execution: The GUI thread and the motor control thread. This allows the motor control and the GUI to operate independently of one another, and to meet their performance requirements without interfering with the parallel processes.

The GUI system is an extension of our previous works. A UI framework had previously been written based on PyGame. This implements a core rendering loop with composable and extendable UI elements, and an interface to update the framework for these elements. We have primitives for labels, buttons, modal buttons (swap between states), sliders, progress bars, sprites, status pips (coloured circular buttons), and elements which are composed of other elements. These expose an update (refresh internal state), render (draw on screen), and handle click/drag (update slider position) methods. They build upon a debouncer primitive and our core entity framework, which is a superclass that provides default implementations for each of these methods (reducing boilerplate for elements which dont need every method). The run-loop handles setting up the program for operations on the pi (setting the display output, mouse drivers, etc), as well as handling events, refreshing elements, and redrawing the UI elements. This section is fairly straight forward, and more specific details on this can be found in previous ECE 5725 labs.

Figure 8: UI Elements

The ui (as pictured above) has 4 labels, 1 for each servo, 4 status pips (which are modal buttons consisting of an active status pip (green-active, yellow-stall, red-disabled)), 4 slider/progress bar combos, a modal start/stop button, and a quit button which exits the program. The UI approximately follows a model-view-controller model, with the view being the UI elements, the controllers being the UI thread and the motor control thread, and there existing dumb data container models, which are periodically synchronized with the microcontroller. The UI system is explained above, but the motor controller thread is more interesting.

The shared states between the motor controller and the UI are the data models, AnalogIn, DigitalOut, PWMOut, and Actuators. These are container classes that store the data used for the UI state and the motors, which use python properties to transform the data from (for example) the 0.3-3.0v range of the analog inputs into the 0-1 range of the progress bar. This allows for a natural programming model, where repetitive transformations are transparently performed to the data, and the scope is reduced for bugs resulting from a failure to transform inputs correctly.

Both the motor-controller and the UI hold references to these models and updates them as needed, with the motor controller calling a refresh on these models once a cycle (avoiding the overhead of redundant communication with the microcontroller for every operation). Each model then sends a request along the serial channel and awaits a response (if needed), either updating the stored data on the microcontroller, or updating its internal view of the state of the microcontroller. The serial channel is implemented using the PySerial library running a 57.6kbps baudrate connection. It uses a request-response model, where it sends a request and expects a response (empty string or a numeric value) rather than a fully asynchronous approach. This is reasonable given the updating is deferred to a separate thread and doesnt block the main GUI thread.

Figure 9: Motor Control Speed Curve

Calibrating the motor control curves (acceleration and speed) proved to be a fairly complicated problem, as the initial approach of bounding speed to the square of the distance of the motor from its intended position gave a very undesirabe, slow acceleration curve and top-end speed. Many iterations were attempted, and we finally settled on the above curve, which reaches maximum speed fairly quickly but still offers gradual startup and shutdown to avoid overshoot.

On the software side, there were several interesting challenges faced by this project. One of the first was managing performance in the core loop. Due to limitations on the microcontroller side, we were bandwidth constrained on the serial link between the host (Raspberry Pi) and the microcontroller. Many issues stemmed from this problem. The first that was encountered during the initial design came from the numerous reads and writes to the I/O device that were synchronously performed in the core rendering loop. Since there could be multiple reads to a single pin per cycle, this significantly affected performance during the software loop, causing refresh rates to drops in PyGame down to single digit FPS, which was unacceptable.

There was no magic bullet solution to this problem. The first step that had been taken was increasing the baudrate of the system from 9600bps to 57.6kbps, which did significantly improve performance in terms of motor control refresh rate. However, this still left the GUI rendering loop far below an acceptable standards. Consequently, the control signals were moved over to refreshing in an asynchronous model, with a second thread running in the background and periodically refreshing the model objects on the client side and the microcontroller (as described above). This fully decoupled the motor control logic from the GUI refresh logic, allowing both to perform far more effectively.

After these issues were solved, another issue that was discovered was with the serial system. Around 0.1% of requests to the micro-controller were corrupted. Eventually, this was tracked down to being caused by the 8 byte serial receive buffer on the micro-controller overflowing and dropping characters. The solution was to move from a fully custom serial command parser to the micro-controllers streaming serial command parser, which is included as part of its standard library. This provided all of the required functionalities, with the benefit of being far better tested than the bespoke command parser. This new parser has been completely reliable for our use case.

There were also issues which werent related the serial channel, namely due to near abandon-ware status of PyGame. Pygame has not been properly ported to python 3, and exhibited a series of extremely strange issues when operating under load. This was resolved by backporting the system to python 2, rewriting the code as needed. Code quality was lost, but the tradeoff in stability which justified the decision. The final issue was a mysterious and inexplicable hard crash of the operating system when running the system directly on the Raspberry Pi. This was eventually tracked down to having being caused by the usage of the RT kernel, and was solved by reverting to the standard kernel.

Validation involved testing each software component individually. First, the GUI as it was written, followed by the motor control system as it was implemented. Each of the UI elements used were manually tested as well. The system was given commands in a stochastic fashion, and its resulting actions/outputs were observed and validated. The system was then gradually tested with stand-in components for the real hardware, until finally being tested as a fully integrated, monolithic unit. All performanc parameters were achieved.

Overall, solving these issues has led to a more robust and stable design. We are pleased with our resulting system.


The primary goal of this system was to create a closed-loop controller for high-load DC-motors with a power capacity of 30A/24V (0.72kW) per channel. This result was achieved.

The closed-loop controller was successfully developed by creating an embedded operating system on the Raspberry Pi, which effectively manipulated the control outputs, using serial communication, of the external hardware I/O device (KL46Z). Adjustments made to the motor position assignment via the user-interface were successfully represented by the control signal outputs based on the current position. Additionally, when power was removed from a motor, the stall routine successfully detected the motors lack of responsivity and cut-off the PWM-output. All user-interface functionality was tested and performed as expected.

The external amplification circuit design exceeded the required power specifications by 6V (corresponding to an additional 180W) while meeting the current specifications. The circuit effectively powered all 4 motors, simultaneously, at the maximum load that was feasible given Cornells lab power supply equipment. Components handled aggressive directional switching stress tests without failure. The negative effects of back-EMF were also nullified through the appropriation of flywheel diodes. Stress-testing of the motors via manual resistance to actuator position was futile in negatively affecting any dynamic characteristics, and all position changes on the piTFT were successfully achieved under every test conditions.

The system performed as intended and all outlined objectives were met.


The results of this project met all outlined objectives, yielding the successful development of an embedded software/hardware device capable of closed-loop control of high-load DC motors. The power supply for the DC motors may range anywhere between 3.6V-30V, and the load-demand of the DC motors can range up to 30A per channel.

Throughout this project, we discovered that the use of high-inductance devices under switching applications yields hazardous back-EMF to connected hardware, and can disable and/or damage connected I/O ports. Consequently, the use of diodes was well-implemented in order to drain this excess voltage and protect peripheral components.

Many issues were encountered throughout the implementation of the software, and solutions or workarounds were developed for each of them in turn. In general, the serial link is one factor which still has room for improvement, but overall, solving these issues has led to a more robust and stable design. We are pleased with our resulting system.

Future Work

Given additional time, we would likely explore the closed-loop control of free DC motors that were not implemented within actuators (actuators require very little control finesse). Instead, we would attempt to control gearbox-based DC motors, which represent additional challenges in optimizing control output to avoid overshoot and instability. However, given the intended use of this circuitry (for actuator-based applications), the current system yields satisfactory performance.

We would also like to move more of the control loop to the microcontroller. Although the python code is reasonable for a prototype, better response performance could be achieved.

Work Distribution

Generic placeholder image

Project Picture

Generic placeholder image

Adam Halverson


My contributions to this project were primarily in the system-level design, assembly, and testing. To begin the project, I discussed with my partner the software/hardware capabilities we required to be successful and ensured we were both clear in our forward paths. My initial efforts were focused on designing an end-to-end system that was capable of meeting the desired functionality specifications given by our project description, all while being within budget constraints ($100). During this time, I also worked with my partner in selecting a suitable hardware I/O device, one which offered the sufficient functionality while remaining compatible with the RasPi. Once we had selected the appropriate hardware and electrical components, it was my responsibility to order, configure, and test my amplification-circuit design for functionality. When feedback issues were encountered in the external circuit, I researched the cause and remedied the problem through additional design work. Upon success, I built the rest of the circuit and integrated the full system with our DC motors and hardware I/O system. At this point, I worked with my partner and his software-development efforts to ensure an appropriate control-scheme was achieved for the motors and that end-to-end functionality was feasible. From this point on, work was entirely cooperative from both parties in debugging the hardware/software in order to achieve a fully-operational end product.

Generic placeholder image

Nishad Mathur


Responsible for designing, implementing and testing the software for the system. We implemented the control loop in conjunction with one another and I took responsibility for handling the UI, microcontroller work and overall software architecture. Testing of the software system in isolation was primarily my task and we cooperatively tested the integrated system, adjusting and implementing new functionality as needed.

Parts List

Total: $89.60


[1] "NXP FDRM-KL46Z Microcontroller" NXP.com, Accessed: 11/17/2017
[2] "N-Channel MOSFET #FQP30N06L" ServoCity.com, Accessed: 11/17/2017
[3] "SPDT Relay #JQX-15F" ServoCity.com, Accessed: 11/17/2017
[4] "115 lb. Thrust Heavy Duty Linear Actuator Specifications" ServoCity.com, Accessed: 11/17/2017

Code Appendix

Code can be viewed here.