RCB v0.01
The plan: a full humanoid robot, progress: hand…
I started this some time ago after seeing some really cool guy on X build something similar, and I thought “I can build that, what could go wrong”, surprisingly things didn’t go really wrong, that wrong at least.
Inspiration
Said cool guy is called Lethic, and this project is called RX1, this is what he built.
What attracted me the most is how simple the design is, it wasn’t that hard to replicate (badly) on my own in CAD.
My Design
I decided this would be my summer project, so I started working on a similar CAD design, I tried to copy what he had done so I can be sure it would work, I wouldn’t want to waste my time in research and development right now, and he seems to know what he is doing.
I designed a basic finger with a tip that moves when the finger gets pulled in.
It also has a screw in the base so you can connect it to the main body. These fingers then get connected to the servos with an attachment so they can be moved up and down.
The design was originally made for M2 screws, but after searching 6 or so stores in Jordan the smallest I could find is M2.5, so I changed my design accordingly.
The Setup
Now that we have the design we can start actually making it a robot, I chose parts mostly based on availability. First we have the servo driver, for that I picked up an Adafruit PCA9685, this driver communicates with the Arduino over I2C, so it only uses two pins, SCL
and SDA
which makes connections quite simple. I also picked up 6 MG90S servos because they are 3 times stronger than the SG90s for 1.4x the price, so it’s a good deal. And finally of course an Arduino UNO because that’s what I had.
Additionally I got a step-down transformer that can handle up to 15 amps, I got tired of switching AA batteries and this is much more convenient.
Code
I tried to keep the Arduino code as simple as possible and control the rest with Python, I set it up in a way so that it receives 3 bytes by serial, one for the index of the servo, and two for the pulse length value of that servo.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
#define SERVO_FREQ 50
#define SERVO_NUM 16
#define u8 uint8_t
#define u16 uint16_t
u16 servos[SERVO_NUM];
void setup() {
Serial.begin(9600);
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ);
delay(10);
}
void loop() {
if (Serial.available() >= 3) {
u8 index = Serial.read();
u16 pulse_len = Serial.read() << 8; // Read high byte
pulse_len |= Serial.read(); // Read low byte
if (servos[index] != pulse_len) {
pwm.setPWM(index, 0, pulse_len);
servos[index] = pulse_len;
}
}
}
And as you can imagine the rest is simple, from Python we send a serial signal and it gets executed. I still have to do some work on this, like sending an acknowledgement signal from the Arduino after it sets the PWM successfully, but that is a problem for future me.
In Python I made a simple class for the hand and a simple API so I can set the finger position between the min and max pulse length using a value from 0 to 1
# hand.py
import serial
import struct
import time
class Hand:
def __init__(self, port="/dev/ttyUSB1", baud_rate=9600):
self.serial = serial.Serial(port, baud_rate)
self.servos = [
Servo(0, 225, 500),
Servo(1, 250, 350, inverted=True),
Servo(3, 250, 350),
Servo(2, 330, 435, inverted=True),
Servo(5, 220, 325),
Servo(4, 360, 465, inverted=True)
]
def move_servo(self, index, position):
if 0 <= index < len(self.servos):
self.servos[index].move(position)
data = struct.pack('!BH', index, self.servos[index].current_pl)
self.serial.write(data)
time.sleep(0.01)
print(f"Sent - Servo: {index}, PL: {self.servos[index].current_pl}")
def close(self):
self.serial.close()
class Servo:
def __init__(self, index, min_pl, max_pl, inverted=False):
self.index = index
self.min_pl = min_pl
self.max_pl = max_pl
self.inverted = inverted
self.current_pl = max_pl if self.inverted else min_pl
def move(self, pos: float):
if not 0 <= pos <= 1:
raise ValueError("Position must be between 0 and 1")
diff = self.max_pl - self.min_pl
if self.inverted:
self.current_pl = int((diff - diff * pos)) + self.min_pl
else:
self.current_pl = int(diff * pos) + self.min_pl
return self.current_pl
And finally I wrote a simple GUI around this class, it looks something like this.
And I can configure all of these buttons to do pre-set animations using a JSON file, the format I came up with looks something like this. The last number after the servo positions is for adding a delay so the fingers have the time to reach the target position.
(not to brag but it supports hot-reloading)
{
"Fist": [
[[0.80, 0.00, 1.00, 1.00, 1.00, 1.00], 0.5],
[[0.80, 0.46, 1.00, 1.00, 1.00, 1.00], 0]
]
}
Hardware
With all that said, the final setup looks something like this.
And you can see it running on my X account.
To Do
Well, I have a long list so let me list it here.
- Screwing in the servos after inserting the ones in front of them is hard, so I have to change the screw position so it’s accessible from both sides.
- The distances between the servos and their attachment is inaccurate, that has to be fixed too.
- The fingers are too thick with the M2.5 screws, I have to make them thinner.
- Better cable management, put a hole next to each servo.
- Better tolerances for the fingers overall.
- The base looks ugly, do something about it.
After I finish these I will move on to making the wrist and the elbow. One thing I can say for sure is that I gained a lot of respect for hardware designers, it’s a hard job, but a fun one.