/ QUADRUPED

Quadruped Robot Update 1!

Since last summer, I've been wanting to start on a fairly ambitious project: building a quadruped robot that can walk and do cool static movements.


I didn't have much time to start back then as school was about to begin, but over winter break, I finally had time to truly start this thing. First, I decided to model everthing out in Fusion 360, a CAD tool that would allow me to 3d print the parts out later. I also began finding and buying parts for testing, including motors, bearings, buck converters, and some teensies with shields. By the end of the break, I had all of the mechanics figured out, and had been able to connect a teensy LC to a USB host 2.0 shield using this library . Now two weeks later, I've begun programming movement on one leg, specifically vertical translation of the foot. The foot stays directly underneath the shoulder joint with input from a ps3 controller!

Best of all, the math is really simple; It's all high school trigonometry!

Here's the "diagram" I'm going to refer too:

The main idea is to use the law of cosines, which allows you to relate three angles of a triangle to one side: C2 = A2+B2 - 2ABcos(c). In this case, the goal is the move the foot vertically by inputing a demand length from the foot to the shoulder (in the diagram, length C). Since lengths A and length B are constant (the literal length of the parts), it is possible to calculate the angle c as long as a feasible demand C is provided. You simply need to rearrange the formula like this: c = cos-1( C2 - B2 - B2-2AB ). After that, the math is easy; cunningly, A = B, so the triangle is isoscles. This makes a = b too, so you can solve for b by using the fact that angular sums of a triangle are 180˚. a = (180˚ - c)2. After that, tell the motors to go to b and c, and you're done!


Here's most of the code that deals with this math:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void Kinematics::solveFtShldrLength(uint16_t controllerInput) {
  
  // Angles need to be mapped to motor zeros because servos don't turn 360 degrees
  if ((_legID == LEG_2) || (_legID == LEG_3)) {

    // Map controller input (0-255, 1 byte) to demand shoulder-foot length in cm
    uint16_t demandShoulderToFoot = map(controllerInput, 0, 1023, SHOULDER_FOOT_MIN, SHOULDER_FOOT_MAX);

    // Use the Law of Cosines to solve for the angles of motor 3 and convert to degrees
    double demandAngle3 = acos( ( pow(demandShoulderToFoot, 2) - pow(LIMB_2, 2) - pow(LIMB_3, 2) ) / (-2 * LIMB_2 * LIMB_3) );
    demandAngle3 = lrint( (demandAngle3 * 180) / PI);

    // Use demandAngle3 to calculate for demandAngle2 (angle for M2)
    double demandAngle2 = lrint( (180 - demandAngle3) / 2 );

    // Calculate final demand angles suited to motors
    demandAngle2 += M2_OFFSET;
    demandAngle3 = (M3_DEFAULT_ANGLE - demandAngle3) + M3_DEFAULT_ANGLE + M3_OFFSET;

    // Constrain motor angles and round the output to reduce noise
    demandAngle2 = _applyConstraints(2, demandAngle2);
    demandAngle3 = _applyConstraints(3, demandAngle3);

    // Set live motor angles to the newly calculated ones

    // motor 2:
    motor2.angleDegrees = demandAngle2;
    motor2.angleMicros = _degreesToMicros(motor2.angleDegrees, motor2.calibOffset);

    // motor 3:
    motor3.angleDegrees = demandAngle3;
    motor3.angleMicros = _degreesToMicros(motor3.angleDegrees, motor3.calibOffset);
  }
};

One of the things I'm most impressed with is the strength of this leg. The datasheet for these motors specified them to be 20kg-centimeters, and when I originally chose them, it was because they were easily accessible and I estimated each motor to be strong enough to lift length A and B (12.5 centimeters). This gives each motor a predicted strenght of 1.6kg at the end of the lever, however I assumed that a significant portion of this power would be lost due to the weight of other motors on a certain motor's lever arm. For example, the motor moving the entire leg in the left-right direction (refer to the diagram above) needs to carry another motor, which moves the leg in the up-down direction. When I was planning, I tried to reduce this effect by placing the motors as close together as possible. And it payed off: my leg can push on the ground with a strength of nearly 1.5kg!

Problems...  so far:

1:  Parts keep breaking. More specifically, one part keeps breaking; the one that holds the x-axis motor (the motor that moves the entire leg horizontally, from the perspective of the diagram image earlier). If they don't break, they don't grip the motor axle; the motor turns, but nothing else happens.

Update:  this problem is fixed! Only took me six iterations...  (the sixth one is in the robot currently). Newer versions progress right.

2:  The PS3/PS4 controller connection is bad. I've been connecting to the main microcontroller (teensy 4.1) from the controllers using this USB host 2.0 library and a mini USB 2.0 host shield. Right now, the library hasn't been updated to work with the teensy 4.1, so I've been running the usb host stuff on a teensy LC and communicating the data to the main teensy over I2C. At first I liked this, since the controllers are fun to use and the bluetooth connection was relatively stable. However, over time I found this to be clunky; intial setup was slow, a gazillion jumper cables plagued my desk, and long-term connectivity issues became worse. I've now decided to get rid of this all together, and possibly build my own controller with two LoRA radios. That will probably require a custom pcb, which may take me a while to finish (my AP chemistry final is two weeks away, and then the hardest unit in the class!).

Here's another render of the robot from the back:

That's all for now!