(Short clip of driving outdoors)
As before, this project is about making a "drone-like" model car which gives the operator a video feed, some telemetry, and which communicates with a single digital radio. At the heart of it is a Raspberry Pi 3. It deals with messaging through the Wifi interface, controlling servos, and communicating with the motor controller, to name the most important tasks.
Illustrated in the following diagram.
Red represents power supply, black lines data flow, and yellowish major components. The box labeled Custom "Shield" is a circuit board which sits on the 40 pin header of the Pi and bears the indicated components. Besides the obvious, there is a status LED which blinks differently depending on the state of the operating system - shut down, rc car processes running, or processes not running. The "shutdown" button triggers shutdown of the OS, as the name implies. With IMU I refer to a AltIMU-10 v4 board from Pololu. I added it for the possibility to present the heading, or some sort of attitude indicator to the user.
The next diagram shows the processes running on the Pi.
There is a central Python script, here named Control Process, which handles messaging and servo control. You read that right, Python. However, the speed with which it could be developed made it worthwhile to write rather than going straight for a C++ implementation. Fortunately it runs fast enough.
RX and TX refer to the processes pertaining to the wifibroadcast software. They use stdin/stdout to exchange data. However, I find it convenient to launch them independently of the other processes. Other processes should just be able to connect to them when both parts are available. I enabled this by connecting RX/TX to socat which opens sockets other processes can connect to.
Raspivid, on the other hand, streams directly through stdout/stdin to TX since this part of the system is very simple and stable - at least from my user perspective. So no need to control these two independently.
All the processes are managed by systemd - much like in the basic wifibroadcast project. There is a service file for each of them. A custom .target file helps to bring everything up automatically in a well defined order.
(Code is on GitHub, if you dare to look)
Details on essential and/or changed components follow.
The main goal after completing the last iteration of the vehicle was the incorporation of a new drive system. There are several reasons for this, namely:
- BLDC motors are considered better than brushed variants. Higher efficiency, less wear, for instance. So it would be nice to be state of the art.
- They can come with integrated position encoders. This is useful again for several reasons. For one because speed measurement and control is desired. My attempts to retrofit the motor I already had largely failed. Also the motor has to fit the chassis and BLDC motors designed for toy cars do.
- I'm interested in power electronics and embedded programming. Therefore I wanted to attempt to design a custom motor driver.
The controller got a project site of its own. I refer there for details.
The car is powered by a single 12 V battery. The motor controller has a small linear regulator to supply its micro controller. In contrast to before, the board on top of the Raspberry Pi now carries a regulator for the Pi and one for the servos. I employ identical cheap switch mode buck converter boards with 3 A output bought from Ebay. My design has two of them because a 2 A supply is recommended for the Pi and the servos can draw significant current, too. In particular the more powerful steering servo. Moreover, voltage fluctuations induced by servo operation should not have a negative impact on the Pi. The Pi has a dedicated USB port for power. However, in my case it was simpler to power it through the 5 V pin on the 40 pin header.
Previously I described by ASCII based messaging protocol with which commands and other data are exchanged between control station and car. While simple and mostly human readable, I ultimately did not like it. Mostly because it becomes unsafe and clumsy to use when things get more sophisticated. To explain, there was no schema, no types, no API. Just raw numbers.
But this can be helped. First let's consider (de)serialization, i.e. turning a data structure into a byte stream to go over the wire. There are good technologies out there. I picked Protobuf. The Protobuf compiler takes a formal message definition and auto-generates code to access the data, parse it from a string, and encode it. In addition to the safety this makes it easy to add more functionality. For instance adding messages for setting parameters.
But that is not all. Unlike say, TCP, the serial link with the motor controller nor Wifibroadcast know the concept of a message. Just raw bytes. Therefore, when transmitting over such channels, the beginning and the end of each message must be clearly identified in one way or another. One might send a header with magic numbers and the message length. I found an interesting alternative named "Consistent Overhead Byte Stuffing" (COBS). Basically, it rewrites the byte stream in a clever way, such that messages are delimited by zeros.
Another important aspect is checking for transmission errors. Wifibroadcast does not do this. In case of video we deliberately delegate the problem of dealing with errors to the video decoder. With control messages the situation is different. Therefore, I simply compute a CRC8 checksum and append it to the serialized message. The receiver also computes the checksum and discards the message if the numbers mismatch.
The motor controller reports
- battery voltage
- average phase current
- motor RPM
- instantaneous PWM duty cycle
- temperature near the MOSFETs.
- Moreover, I figured it would be enlightening to record snapshots of the phase current profile over a millisecond or so. Oscilloscope style. This is certainly reasonable as to my attempts to implemented current/torque control.
The Pi itself monitors
- system load, mostly for diagnosis, because too large load will cause video lag and load depends on video content - and my programming ;-)
- And Euler angles from the IMU. Fusion of raw sensor data and orientation estimation is handled by the 3rd party code minimu9-ahrs. Although it would have been interesting to understand the theory and implement it myself, I wanted to finish the project. So I did not bother.
All of this data is sent periodically as Protobuf messages to the control station. In addition to the telemetry, the following is also displayed to the user
- the signal strength obtained from the wifi driver,
- data rate of the received signals,
- and there is a blinking light indicating reception of packets.
Overall it is quite simple. The Pi receives messages with desired speed, steering deflection, and camera yaw. The speed value is forwarded to the motor controller. The servos are controlled directly from the Pi, i.e. their signal wires are connected to the 40 pin header. I am using the pigpio library to configure the PWM signal at the pins. This library even implements special convenience functions for servo control.
In this projects I implemented a number of improvements and extensions over the previous iterations of this model car. At the very least I arrived at another working solution which can be deployed outdoors. And I am rather quite happy with that.
But so far I did not have a chance to try it on more challenging terrain, like steep climbs, rocks, and so on. Hence I am uncertain about its performance there. Which I hope to change when I find a suitable spot.
Indeed, I am fortunate that the initially fabricated hardware already does well. However, there are some issues. Most disturbing at present maybe the deliberate lack of consideration for casing during the design of the circuit board. The connectors, cables and buttons are in places that makes it impossible to devise a satisfactory protective cover. I find the one I made for now rather comical.
In spite of that, the next step in this line of project will most likely be a remote control. I still use my laptop - put on the roof of my car so I can operate the game controller with both hands. A more portable solution is desired.