Hello everyone!
First, I’d like to say that I’ve made the 3D models of the Robomate V1 publicly available. We are working on turning the robot into a kit that you can assemble yourself.
You can find the control code in the fantastic project from the SimpleFOC team at this link.
After my previous Reddit posts, I received a massive number of questions about how my robot works, so I’m continuing to share more details.
Today, I want to talk about how the robot is controlled. Honestly, I can’t imagine anything more exciting in life than reading about nested PID controllers, so take a look at the code and let’s get started! 😃

I won’t explain what a PID controller is — there are plenty of resources online for that. Instead, we’ll focus on more interesting things. Visualizing nested controllers in a diagram has always helped me understand them better.
The most important thing is what we want to control — in this case, velocity. We don’t control the angle or torque directly because we want the robot to maintain a velocity of zero when no input is given. However, the angle or torque might not be zero when the velocity is zero, especially if the robot remains upright.
By comparing the desired velocity of the robot with its actual velocity (averaged between both motors), we get the velocity error, which is then fed into the Velocity PID controller. The output of this controller is the target pitch — the angle at which the robot is likely to achieve the requested velocity.
Example: If the pilot wants the robot to move forward at a high speed, the robot should tilt forward and hold that tilt. By maintaining this forward tilt, it will naturally keep moving forward.
After this, we repeat the process:
- We compare the current pitch with the target pitch to get the pitch error, which is fed into the Pitch PID controller.
- The SimpleFOC motor controller is set to
MotionControlType::torque, so the Pitch PID is tuned to output the necessary torque to maintain balance.
This setup is enough for the robot to stand in place and move forward or backwards. To enable turning, we simply add torque to one motor and subtract the same amount from the other.
Now the robot can move in any direction! But there’s still plenty of room for improvement — I invite you to brainstorm and discuss new ideas together!
Thought-Provoking Questions:
- Why is it crucial to avoid pushing the robot to its maximum motor speed while driving?
- The integral coefficient in my PID controllers is set to zero — why? What effect does this have?
- In the basic version of the algorithm, when moving forward, the robot might slowly turn due to motor differences or varying surface grip. How could we improve the controller to fix this?
- If the robot falls over, how could we make it stand up from any position with just a few extra lines of code?
- What would happen if the robot tried to drive up a halfpipe? Could we change this behaviour?
Looking forward to your ideas on Discord! 🚀