The idea for Nano began in the summer of 2013 when it was still called “Egg Yolk.” The initial concept was to create an entry-level self-balancing car (because I signed up for the balance group in the school’s Freescale competition, and I was preparing for it). The preliminary idea was to make it based on Arduino, controllable with a PS2 controller, able to balance and move, and ideally, it should be cute.
Actually, that was also my first time coming into contact with and using Arduino. At that time, I was a poor teenager… I bought a domestic mini pro bare board and felt a bit happy, but not long after, I messed up the power connection and ruined it…
The first version of Egg Yolk was made with the mini pro. Since there were no 3D printers available at the time, all connections were basically made with universal glue, resulting in a very rough overall structure. How to say, this might be the steampunk style (not really).
After posting the information about Egg Yolk on the forum, it attracted a lot of attention. Many classmates were successfully led into the self-balancing car pit… More than a year later, people kept asking me related questions. Because of this, I felt ashamed for having dealt with everyone for so long with so little content, and combined with my deeper understanding of self-balancing systems… I decided to improve many shortcomings of the first generation Egg Yolk and started designing and making version 2.0 of Egg Yolk.
The dissatisfaction with the first generation mainly focused on its appearance (after all, appearance is combat power), speed control (at that time, I hardly used the speed loop and could only rely on constantly manually adjusting the balance point to move), expandability (the peripherals of the first generation were too simple and did not fully utilize the performance of the processor), and appearance (you can see I really care about this…).
In the end, there was Nano!
——————————————————————————————————————–
Next, I will introduce a detailed tutorial on making a Nano, including some principles of automatic control and some problems and experiences I encountered. It is worth noting that achieving complete control of a self-balancing robot requires a lot of parameter tuning, so this tutorial will try to introduce the principles and debugging methods in a simple way, but you still need to have a certain foundation in electronic production, be familiar with using Arduino, have strong hands-on skills, and possess a firm geek spirit. Good luck! 🙂
#Principles
A self-balancing car is a typical inverted pendulum control model. What is an inverted pendulum? I believe everyone has seen a regular pendulum.
When an object leaves the vertical balance position, it will be subjected to the combined force of gravity and the tension of the suspension line, driving the heavy object back to the balance position. This force is called the restoring force, and its magnitude is given by:
F = −mgsinθ
When the angle of deviation θ is very small, sinθ ≈ θ, so the restoring force is proportional to the angle of deviation and acts in the opposite direction. Under the action of this restoring force, the pendulum will undergo periodic motion.
Considering the pendulum moving in the air, due to the damping force of the air, the pendulum will eventually stop at the vertical balance position. The damping force of the air is proportional to the angular velocity of the pendulum and acts in the opposite direction. The greater the damping force, the faster the pendulum stabilizes at the vertical position.
Now let’s look at this equivalent model:
Our car model is actually equivalent to an inverted pendulum. It can be seen that at this moment, the force of gravity on the object acts downward. When the object deviates by a little angle, the force of gravity will act in the same direction as the angle of deviation. If the wheels do not move, the pendulum will quickly fall down.
To solve this problem and make the inverted pendulum stable in the vertical position like a simple pendulum, we have two methods: (1) change the direction of gravity (2) add additional forces so that the restoring force acts in the opposite direction to the displacement.
Clearly, only the second method is feasible. Therefore, we control the acceleration and deceleration of the wheels based on the deviation angle of the pendulum, so that in the coordinate system of the car (non-inertial frame), the pendulum will be subjected to additional inertial forces, ultimately allowing the pendulum to balance.
To put it simply, when we notice that the car is tilting forward, we quickly accelerate the wheels forward. When we notice that the car is tilting backward, we quickly accelerate the wheels backward. As long as this process is done precisely and quickly enough, we can achieve self-balancing of the car.
#Hardware
In the principles mentioned, we need to control the acceleration and deceleration of the wheels based on the angle of deviation of the car. Therefore, the modules needed here include:
Arduino main control board – You can choose any board you are familiar with, I recommend the nano, it is compact and easy to download.
Gyroscope and accelerometer module – Used to measure tilt angles, I recommend MPU6050, it is cheap and easy to use.
Deceleration motor – Size is self-defined, but an output speed of around 300rpm is more suitable. It is worth noting that the motor must have an encoder or code disk for speed measurement, either single-phase or two-phase.
Motor driver – For ordinary-sized motors, I recommend the TB6612 driver chip, which is more efficient and less prone to heat than the L298 (average current around 1.2A, for larger power, choose L298 or other drivers); for mini motors, you can use the L9110s module, which is cheap and compact.
Bluetooth module – Used for communication with mobile phones, either from a module or a master-slave integrated version is fine.
Button – Any two-legged button can be used for some settings.
Battery – If powered by lithium batteries above 5V, it can be used directly. However, if using a mini car powered by a 3.7V small battery, you need to pay attention to add a DC boost module; otherwise, Arduino may not work properly at 16MHz.
Additional expandable modules can include:
Ultrasonic module – Can be used for distance measurement and obstacle avoidance, SR04 is quite common, a smaller option is RCW-0001, of course, smaller ones can also be DIY with transceiver.
Distance sensor – A series of sensors from Sharp, which are a bit more expensive than ultrasonic modules, but perform better.
OLED display – Used to display status data, a 0.96-inch display with a resolution of 128×64 is excellent. It is best to buy an SPI interface, as I2C may conflict with MPU6050 (this may be an exception, theoretically, the addresses should not conflict, but I have not delved into the specific reasons).
Buzzer – To make the car sound, it is often better than staring at an LED, I recommend using an active buzzer.
Camera – To be precise, it is an infrared light sensor. Due to the performance limitations of Arduino for image processing, general cameras cannot be used.
In addition to general welding tools and hand tools, a hot glue gun will become your right-hand assistant for DIY. As for structural parts, students with 3D printers can directly download the STL files at the back for printing. Students without printers can also find 3D printing service shops on the omnipotent orange website. One more thing to note is that the small motor I used at that time was modified by myself; it was originally a zoom motor from a digital camera, so it is very compact and can accommodate a code disk. After modifying the gear ratio and increasing the output shaft, I used it for the small car, but it seems that this model is no longer sold now…
However, later I accidentally found a more suitable N20 deceleration small motor, which performs much better than the modified one, with less backlash and friction loss, and the output shaft can be directly connected to the wheels, and it is equipped with Hall speed measurement. It looks like this:
Students interested in making a super mini balancing car can choose this one. I won’t provide the TB link… just search for “N20 deceleration motor mini” on the orange website, and you’ll find it.
#Software
The software section mainly introduces the PID algorithm, which can be said to be the core of the entire project program. The effectiveness of its use determines whether your car can self-balance and how stable that balance is. There are many introductions to the PID algorithm and theoretical analysis online, so I won’t go into detail here; you can search for it yourself. The introduction based on the mathematical model is a bit difficult to understand, so this article will briefly explain PID and its usage from the perspective of control theory.
PID is the abbreviation for Proportional-Integral-Derivative, but it does not necessarily have to possess all three algorithms simultaneously; it can also be PD, PI, or even only P control. Below, I will introduce the meaning of each parameter:
First, it is necessary to clarify a fact: to implement the PID algorithm, closed-loop control must be present in the hardware, meaning feedback must be available. For example, to control the speed of a motor, a sensor that measures speed must be present to feed the results back to the controller. In a self-balancing system, there are commonly three control loops – angle loop, speed loop, and steering loop.
Can everyone imagine what the feedback components of each closed loop are? That’s right, they are the IMU (gyroscope + accelerometer), encoder, camera (or other components that can determine position, such as a gyroscope, magnetometer, etc.).
P (Proportional): Taking the car line following as an example, now we need to make the car follow a trajectory. Using the PID algorithm to control the direction loop, let’s assume the feedback sensor is a camera. During the car’s movement, there are several situations:
1. The car detects through the camera that it is on the left side of the trajectory, and the position error value is positive, so it needs to turn right, and the steering value is positive.
2. The car detects through the camera that it is on the right side of the trajectory, and the position error value is negative, so it needs to turn left, and the steering value is negative.
3. The car detects through the camera that it is right in the middle of the trajectory, and the position error value is 0, happily moving straight ahead, with a steering value of 0.
Thus, we find that the output of the steering value of the car can be simply obtained by multiplying the position error by a coefficient, and obviously, the larger the error, the larger the obtained steering value, which meets the requirement. This coefficient is P, and the specific size of the coefficient needs to be determined through actual debugging.
We have the first formula: P_term = kP * error
D (Derivative): Still using the car line following as an example, the same car and line and proportion. During the car’s movement, there are several situations:
1. The car gradually approaches the middle from the left, but due to inertia, it cannot stop! So the car goes back to the right side of the line.
2. The car gradually approaches the middle from the right, but due to inertia, it cannot stop! So the car goes back to the left side of the line.
…
This is not what we expected! At this point, D comes into play. Think about what effect we expect. We hope the car reaches the midpoint, and at this time, not only should the position error be 0, but also the steering speed error should be 0.
So we set the expected steering speed to 0. If the car’s steering speed is to the right, the error is positive; if to the left, the error is negative.
Looking at the previous situation 1, the car’s steering speed error is positive, so we should give it a left steering force in addition to P, to ensure that it does not move too fast when it reaches the midpoint; situation 2 is similar, at this time it needs a right steering force.
In other words, D provides a steering resistance to the car, and this force can also be obtained by simply multiplying the steering speed error by a coefficient. Obviously, the larger the steering speed error, the larger the obtained resistance, which meets the requirement (it is worth noting that the steering speed here is relative to the midpoint, not the steering speed output by the car; it can be understood as the “speed of position change”).
We have the second formula: D_term = kD * (error – last_error)
If the above example is still difficult to understand, consider the previous inverted pendulum model: P is equivalent to the effect of gravity, causing the pendulum to swing back and forth, while D is equivalent to air resistance, slowing the pendulum down until it stops at the midpoint. Ideally, the size of D should allow the pendulum to swing back and forth once and then stop at the midpoint, as if the pendulum were swinging in water.
I (Integral)
Sometimes we find that there are some fixed resistances in the system. For example, when we use PID to control the speed of a motor, if the target speed is very small, the following situation will occur:
According to P_term = kP * error, since the error is very small, the output of P is also very small, and due to the presence of friction, the motor may not start at this time; and according to D_term = kD * (error – last_error), since the motor has not moved, obviously (error – last_error) will always be 0, so D output will also be 0. Then the question arises: unless the target value is changed, the motor will never start…
The role of I is to eliminate such static errors. It will accumulate each error and then multiply it by a coefficient to output. For example, in the above situation, although the error is very small, it is not 0, so in each round of calculation, the I term gradually accumulates the error until it exceeds the critical value to get the motor to start; when the error is 0, the I term will not cause any trouble.
The third formula: I_term = kI * (I_term + error)
Thus, the complete PID calculation is as follows:
PID_output = P_term + I_term + D_term
Running it at fixed intervals gives us the PID algorithm.
It can be seen that the implementation of the PID algorithm is actually very simple, with just a few lines of code, so it is highly recommended to implement the PID code yourself. There are also PID libraries available on the Arduino platform, but I won’t tell you what the library is called; you can find it yourself.
#Manufacturing
If you can understand the above, you can start making it! Here I will take the process of making Nano as an example, but everyone can adjust according to the actual situation.
The online disk keeps having link failures, so all files (source code + STL model files + APP) are posted on the Non-Linear Circuit City (charging 1 yuan to treat me to spicy strips…).
The price may still be under review, so those who need to download can go later.
Let me explain the files inside:
The STL files are the structural parts of the Nano’s head and body. The base needs to be determined by everyone based on the motors they use; just make a base with two motors according to the motors you buy. With the help of a hot glue gun, it should be quite simple.
It is recommended to use version 1.6.5 of the IDE to compile the source code, as there are some differences in the library files in older versions.
First, let’s take a look at the schematic (click to open a large image):
It should be noted that the power lines in the diagram are not connected (one positive and one negative, everyone knows), and the IO can be adjusted by yourself; just change the macro definitions in the program. Also, the encoder can be single-phase or AB phase. I am using a single-phase encoder, which can only measure speed but not direction, so the direction detection is simulated by giving the PWM direction to the motor. If conditions allow, it is better to use a bi-directional one. For single-phase encoders, directly connect the signal line to D2 and D3, which belong to interrupt 0 and interrupt 1, respectively; for AB phase, one line connects to D2 or D3, and the other line connects to a regular IO to determine direction (at this time, you need to modify the contents of the ENCODER_L() and ENCODER_R() functions at the end of the program).
Then start connecting the modules to the shell according to the schematic.
After printing the files, assemble them according to the schematic. The head contains the ultrasonic sensor, Arduino nano board (without pin header), Bluetooth module, two LEDs, camera, and buzzer. Be sure to lead all IO out to the neck area.
This is what it looks like from the front.
After filling the head module, don’t rush to glue it. There are still wires from the body to connect to the main control board, and the internal components can be temporarily fixed with hot glue. By the way, for wire selection, if you have good skills, you can use enameled wire to save space. If you are not sure, use this kind of FC ribbon cable, which is soft and easy to peel, relatively thin, and looks more beautiful when arranged in a row. Dupont wires are really not easy to use…
The body mainly houses the servo (if used), OLED, motor driver board, and boost board. When inserting the OLED, be careful not to use too much force; otherwise, the screen glass is easy to break (don’t ask me how I know this…).
If the wires were led out while assembling the head, they can be soldered to the screen now.
The MPU6050 is at the bottom and should also be fixed with 502 glue or hot glue.
Next is the servo. The servo used is the most common 9g servo. Although there are smaller ones, I am worried that the torque may not be enough. It can be seen that the servo arm drives the connecting rod of the neck, allowing the head to move. I do not recommend using a servo; one reason is that the power of the mini robot’s battery is relatively small, and the servo is prone to freezing when stalled. The other reason is that the weight of the head is relatively large, so changing the center of gravity during movement can easily interfere with balance, and the parameter settings will be more complicated.
Next, assemble the body part from the side. First, fix the boost board with hot glue on one side, then cover it with the cover plate and glue it with 502 glue (the red circle marks a DC-DC boost board).
At this point, the main body is almost complete!
Now you can start making the motor base. One reference plan is to cut an old phone card or bank card to use as a base, then fix the two motors on the base with hot glue, so the base can be glued to the body.
Now you can install the motor driver. I am using the L9110S, which is directly covered on the back of the body.
After installing the back cover, the entire body part is completed!
Finally, for the battery pack, find a thin and light battery and stick it directly to the back. You can also add a battery cover.
This is what it looks like from the side.
Little Nano is complete!
Of course, it still cannot move. So don’t rush to glue the head; first, write a few small programs to test the working status of each module one by one, such as whether the ultrasonic sensor works, whether the 6050 has data, etc. You can directly find the example in each library, modify the defined pins, and see the effect.
Once you confirm that there are no issues, carefully fix each part with hot glue and prepare to start debugging the program.
Open any file in the source program folder, and you will see the entire project code.
The first thing you need to modify is the pin definitions, changing the pins to match your actual connections.
/*********************Pin Definitions*********************/
#define LFT 0
#define RHT 1
#define BUZZER 4 //Buzzer
#define BUTTON 5 //Button
#define LED 11 //Cheek LED
//#define SERVO 13 //Servo, not recommended for small robots, as the current is prone to overload
#define TRIG_PIN 8 //Ultrasonic module trigger pin
#define ECHO_PIN 7 //Ultrasonic module receive pin
Then, in the debugging options above, uncomment IMU_OUTPUT, like this:
Download the program to the main control board, open the serial monitor, press a button, and you can see the output angle data.
If the data is abnormal, you need to check where the problem is. This step is to obtain the natural angle of balance for the robot: manually place the robot upright, which is approximately at the natural center of balance angle. Read the angle data from the serial port, and record this value as the angle_setpoint value, changing line 66 from angle_setpoint = 0 to your obtained value.
Next, comment out IMU_OUTPUT again, uncomment MOTOR_ENABLE, and use the Motor(char LR, int SPEED) function at the end of setup() to test whether the positive and negative poles of the motor are correct. When you give Motor(LFT, 100), the left wheel rotates forward; Motor(LFT, -100) makes the left wheel rotate backward; Motor(RHT, 100) makes the right wheel rotate forward; Motor(RHT, -100) makes the right wheel rotate backward.
Once the previous step is also successful, you can start adjusting the PID parameters for the angle loop.
Let me explain why I defined the parameters with macros here. If you use the mega328p chip, due to the relatively large amount of code in the entire program, it will prompt that the dynamic memory is tight. Therefore, after debugging the parameters, I fixed them with macros to save SRAM.
The overall parameter tuning principle is:
First P, then D. If the motor response is slow (for example, if the motor has many gear reductions), then adjust I. If PD is good enough, I is not needed.
Use a single variable method, adjusting one parameter while keeping the others fixed.
First determine the magnitude, then the value. For example, when adjusting P, start from 0.0001, observe the car’s response, and if there is no effect, change it to 0.001, and so on, until a suitable magnitude is determined, and then start fine-tuning within that magnitude. This way, you have already narrowed down the adjustment range to a small size.
First overshoot and then reduce. That is, all parameters should be increased as much as possible until the system oscillates, and then take a slightly smaller value as the most suitable parameter.
During the debugging process, try to keep the car in a natural state without additional forces acting on it, i.e., try to use wireless debugging. If wired, find a softer wire.
So, what kind of performance during the adjustment process counts as “good” for the car?
In the angle loop, when P gradually increases, the car will begin to exert restoring force. That is, when you push it forward, the car can also roughly follow and move forward, but it still moves very “softly.” Gradually increasing the parameter, the restoring force becomes larger until it becomes so large that the car begins to shake violently back and forth, and a car with better motor performance can even stand up roughly without being pushed.
After slightly reducing the parameters that just caused oscillation, fix the P value in the program and start adjusting the D value. Again, determine the magnitude first, then gradually increase it. During the increase, you will find that the oscillation frequency of the car gradually decreases. When it increases to a certain extent, the car basically stops oscillating. This value is the required D value.
In some cases, due to motor performance, the above PD adjustment may not achieve good results. In this case, it is necessary to add I. After adjusting the P value, set D to 0, and increase I until the car’s restoring force becomes relatively “hard,” then slightly reduce the P value until a relatively ideal upright effect is achieved. Finally, add D, increasing until oscillation occurs, and then reduce to about 70%. This completes the parameter tuning for the angle loop PID.
With the angle loop adjusted, the car can maintain stability. But why does it keep running in one direction?
Because the task of the angle loop is to maintain the angle of the car, apart from that, it is beyond its capability. The angle loop does not care whether the car is balancing while stationary or while moving – if the target balance point and the car’s center of gravity coincide, then the car can roughly remain stationary. However, if not, the car will constantly accelerate in balance until the wheel speed exceeds what the motor can provide, and thus the car will still fall.
Therefore, we need to add a speed loop, using the encoder to measure speed as feedback.
In the speed loop, first confirm that the values obtained from the encoder are correct. In the program, they are stored in count_L and count_R. Print the output while turning the wheels to check whether the data is correct. If correct, based on the adjustment of the angle loop, you can add the speed loop.
In the debugging options, uncomment SPEED_LOOP, and this time we do not need D. The speed loop is purely controlled by PI, and we adjust I first and then P. The corresponding performance is as follows:
First give a relatively small P value (because I is the accumulation of P, if P is 0, then I has no meaning). As the I value gradually increases, gently push the car, and it will move forward and then slowly retreat, indicating that the parameters are taking effect. At this point, you can decide whether to give a larger I value or a smaller one. A larger I value corresponds to a faster recovery speed, which will cause the car to retreat more violently after deviation, returning to the original point in a shorter distance. However, this will reduce the stability of the car. A smaller parameter has the opposite effect, requiring a longer distance to return to the original point but enhancing the ability to resist interference, meaning it will not be easily pushed down.
Using only I will cause the car to oscillate back and forth, gradually approaching the original point. Adding P will eliminate this back-and-forth oscillation. The process of adjusting D in the angle loop is similar; gradually increase it until the car can oscillate once back and forth and then return to the original point and stop. At this point, this P value corresponds to the most suitable parameter for that I value.
Here is a video of a large car I made previously, demonstrating its anti-interference capabilities. You can see that its balance performance is very good.
The process of adjusting PID parameters is roughly like this. In short, it is a task that requires careful adjustment but also has a lot of freedom. The so-called “boldly hypothesize, carefully verify”; after trying a few parameter combinations, you will find the magic point that suits your car~
There is much more to say about parameter adjustment, such as the adjustment form. You can add a few potentiometers to the car, read them using analogRead, and visually observe the impact of continuous parameter changes. Also, for wireless debugging, using SSH terminal serial protocol control will be much more convenient than using serial assistants, etc. But I think the most effective way to understand parameters is to operate it yourself, trying more and comparing. Here is a more detailed video tutorial on parameter tuning, which I highly recommend you continue to watch, as it also includes detailed introductions to the tuning of the direction loop.