Building my own OneWheel
I always wanted one of these things, but at almost $2000 for the XR (dropped to $1000 for the Pint), it was way out of my budget. So, I did the next best thing and came up with this contraption. It’s my attempt at recreating the OneWheel, for under $300.
For the wheel, I went with this 10 inch hub motor, which has specs pretty close to the actual OneWheel. As the brain, I chose an ESP32 dev board. It’s a very popular dual-core microcontroller, with bluetooth and Wi-Fi built in. I also wanted to get some experience working with an RTOS, so a 32bit MCU was a good choice. And to determine how much the rider is tilting, I used an MPU9250 IMU, which connects to the microcontroller with an I2C bus.
For the speed controller, I went with the VESC, which is an open source ESC designed by Benjamin Vedder. It has tons of configuration options, which can all be set through the desktop app. It also has a UART interface, which makes it great for interfacing with an MCU. Vedder wrote a blog post with an example project on how to get the UART interface working. The example project has simple functions for using all of the VESCs functionality, including sending power to the motor, reading speed, battery level and other data, and changing configuration options. The vast majority of his code is platform agnostic, so I just had to implement a few of the platform-specific UART functions to port his code to the ESP32. Once that was done, I had a nice high-level interface to send any command I needed to the motor controller. Sweet!
My next challenge was figuring out how to read data from the IMU to the ESP32. Invensense provides a generic library called Embedded MotionDriver, which can be ported to any MCU platform. The idea is that you implement a thin hardware abstraction layer, consisting of a few I2C and timing functions, and the library provides the rest of the functionality. So rather than sending I2C commands to set the registers directly, you can work with a high-level interface, which makes life much easier.
The IMU has a really useful feature called the DMP, or digital motion processor. It’s able to run a sensor fusion algorithm directly on the chip, which saves computing power on the host MCU, and means that I didn’t have to implement an algorithm myself. The DMP is able to directly output a quaternion, that can be read through the I2C interface. From there, it’s just a matter of using a function to convert that quaternion into Euler angles (x, y, and z axes), and feeding the axis corresponding to front-back tilt into a PID loop.
I won’t go into the details of PID control (see this excellent video for more on that), but the main point is that it’s a control method which takes the output of the system (the IMU reading), and uses it to compute the input into the system (the power sent to the motor). In other words, it’s just the algorithm that takes the tilt reading, and determines how much power should be sent to the motor. There are libraries available which implement PID, but to learn more about it I went through this series of blog posts to write one. It’s very similar to the Arduino PID library, except it’s written in C, and it has a few features I didn’t need omitted.
Now, if you’re familiar with PID, you know that one of the challenges of implementing PID is tuning the controller properly. This involves setting three constants: P gain, I gain, and D gain. The challenge is to set these constants so that the board not only balances itself, but also responds quickly to input, and is smooth to ride. There are more formal methods for finding these parameters, but I prefer to do it the old fashioned way — mucking around with it until it works. In previous iterations, I had to plug the board into my computer and re-flash it every time I wanted to change the constants. As you can imagine, this got very irritating. So for this iteration, I wrote an Android app to tune the PID constants on-the-fly. This also lets me see debugging information while I’m riding.
To write this, I first setup a BLE (Bluetooth Low Energy) server on the ESP32. There’s a lot of code involved in setting this up, but luckily Espressif has pretty good examples, which I was able to modify for my purpose. Mostly, I removed some of the example code I didn’t need, and added the appropriate services and characteristics to the GATT attribute table (See here to learn more about BLE). My architecture is pretty simple, with one main service, and three characteristics: one for PID, one for reading IMU angles, and one for other miscellaneous data (current speed, motor power, battery voltage, etc.) I’ll probably be adding more to this table as I add more features. For the actual communication, the ESP32 gatt libraries provide some nice functions for sending and receiving data.
To write the Android app, it was just a matter of reading and writing to the characteristics I defined above. To make life easy, I used RxAndroidBle, which abstracts away a lot of the complexities in writing BLE related code on Android. So far, I’ve implemented widgets to view and graph the IMU reading, and functionality to set the PID constants. I also have a widget to view the current input speed to the motor. Again, I’ll probably be adding more functionality to this app in the future.
The frame is a pretty simple design made almost entirely out of 1in square tubing. Two pieces of flat bar are used on each of the wheel, to mount it to the frame. A piece of square tubing and flat bar are used to lock down the point where the two sides of the frame meet (where the wheel is mounted), and make it rigid. For the bumpers and footpads, I used some scraps of half inch plywood.
The battery is a 10S2P lithium-ion pack, very similar to something like this. It’s a spare one I got from my workplace, so I’m not too sure on the exact specs. The only problem with my battery was that with the way the cells were arranged, the pack would be too thick to fit in the frame. This was pretty easy to solve though. It was just a matter of cutting open the battery’s protective casing, rearranging the cells, and extending a few of the wires to match.
From previous builds I knew that flashing new firmware was something I’d be doing very often. I thought about implementing over-the-air updates through Wi-Fi, but figured that the simplest solution was to just have a programming connector on the frame. I decided on a 10-pin aviation connector so that I could connect the debugger as well. Through the connector, I wired the 4 USB wires, along with the 5 JTAG wires for the debugger. I also added a DC jack to use as a charge port. Not totally necessary, but I think it’s a nice touch.
While not perfect, it’s definitely rideable, and I’ve put a few miles on it without issue. It’s not quite as smooth and robust as the actual OneWheel, but for something made in my parents’ garage, I’m pretty happy with it :)
I have the (in progress) firmware, and the Android app on Github.
From this awesome forum thread of people building their own DIY OneWheels, I’ve found that a self-balancing app was recently added to the VESC firmware. Lots of people have reported success on OneWheels using that feature, so it’s very likely I’ll be able to remove the ESP32, and have my entire system running on the VESC.
Most importantly though, I need to do something about that janky steel frame. I’ll likely model the actual OneWheel Pint, which has two aluminum side rails, and plastic bumpers and footpads. This time, I’m going to design everything in CAD, 3D print the plastic parts, and hopefully have something that looks a bit more professional. Stay tuned! :)