The Sound of One Arm Tapping
I’ve made some progress on my robot typist, specifically with the arms I got last week. Even before buying the two MeArms, I’d read that putting them together was a fiddly challenge so I saved up all my patience and put on a soothing podcast before I started to build the first one.
I’ve never built anything like it before. I always feel pretty mechanically feeble. Sure, I can put up a shelf, but servos and hooking things up with gears, causes me to shy away. However, this was a really good experience for me, lots of aha! moments. I couldn’t build it from first principles, but building the second one will be easier.
It was an exercise in learning to read multiple different sources of instructions to figure out what I’m actually supposed to do. (I am really glad there were multiple sources.) The official Build Guide for MeArm v1.1 (Instructables pdf) was good, mostly in that there is the mise-en-place for each step, laying out what will be needed in a clear fashion so I can’t connect the wrist bones to the shoulder. However, in the document, there are sometimes low-resolution photos of two clear plastic things attaching together. Without words to describe or multiple angles, that manual is tough to follow in spots. I found that the older Build Guide for MeArm v1.0 (pdf Manual Revision 1.4) with its exploded mechanical diagrams showed what I needed. And then there was the the v1.0 build on Instructables that had pictures from different angles and words to go along with it
I used an Arduino to calibrate the motors. None of the instructions have good explanations on what “calibration” meant so I generally tried to get the servo input to match the angle of the joint. This worked out pretty well. I could tell you more about building the arms but, really, the instructions will get you through.
I do, however, have some code that might help you. I made an Arduino command line tester and put it in github. Start out with IK equal to zero so the system is in servo-control mode. You’ll see the #define in the .ino file. Note: IK stands for inverse kinematics but more on that later, right now you are turning it off so we can ignore it. Build, load the code, start the serial monitor, then you can type the servo library values to each servo to see how they move their piece, ideally before you attach the assembles together then again after you attach them.
My next step was to use a goniometer (ahem, fancy protractor) to measure the maximum and minimum angles for each joint, finding the corresponding servo values.
I have a power supply that shows the current draw; that was incredibly useful for trying to figure out the servo ranges because I couldn’t always tell if the (clear) plastic assembly are hitting another piece of (clear) plastic. Anytime the current would go up, I’d fuss around, trying to figure out why. I found that my elbow servo doesn’t have quite enough torque (oomph) to move up. Instead, it tries to get there, fails, and sucks current. Instead, I should program it to overshoot and then come back down, something I’ll have to remember for later.
I made a table of the data I collected:
Joint | Min | Max | Home |
---|---|---|---|
Base | 0 degrees at servo.write(0) | 180 degrees at servo.write(180) | 90 degrees at servo.write(90) |
Shoulder | 17 degrees at servo.write(20) | 180 degrees at servo.write(180) | 90 degrees at servo.write(90) |
Elbow | 43 degrees at servo.write(60) | 124 degrees at servo.write(140) | 90 degrees at servo.write(77) |
Claw | servo.write(0) is ridiculously open | servo.write(180) is lightly closed | servo.write(170) |
I set the min and max servo levels in the code so I couldn’t accidentally type something damaging to my motors (configuration.h).
Then I started reading about inverse kinematics. This is sorta pointless; in the end, I want my fancy-shmancy Jetson board to use the camera to control the claw location through reinforcement learning so the min and max ranges should be enough. And yet, inverse kinematics seems important so I went on the tangent.
Inverse kinematics is the math and physics for moving the robot arm from where it is to where you want it to be when you can only control the angles of the motors. Imagine you want to touch the ‘a’ key at some position (x,y) on a keyboard. To get there you angle your shoulder, elbow, wrist, and finger to get to the key. Then you depress it by moving some distance down (in the z direction). My little robot doesn’t have so many axis to rotate but I still want to solve the problem of getting the end of the claw to the right position in space (x, y, z) by setting my motor angles (base, shoulder, elbow).
The best overview with math was from LearnAboutRobots.com. I didn’t understand it the first few times I read it. However, once I wrote it down, one step at a time, trying to figure out the next step before reading it, then the process made sense. I’m not sure I could derive it from first principles, and I recognize it is 2D where I need 3D but it is a good explanation.
From there, I went to some inverse kinematics code, some specifically for the MeArm. I had already seen the code in my search for instructions (in the BobStonesArduinoCode part of the mearm-brains-arduino git repository). I read through that a few time, mostly the MeArm and inverse kinematics (ik.cpp) file. I started to add it as a library but that meant modifying the code in the Arduino library folders. Instead, I added it as regular code because I knew I wanted some printf calls, and to limit my servos inputs so I didn’t burn up the motors. Then I put all of the its configuration information into a header so I wasn’t changing the source to make it work for my device.
I made a separate compile mode, controlled by a #define (IK) in the .ino so I could shift between direct servo control and letting the MeArm control the servos.
It did not work. I mean, it sort of worked but not well. I spent a lot of time typing in random numbers, hoping they would work, trying to map out valid x, y, and z values. I spent a long time looking at the MeArm constructor, trying to figure out how to shove my parameters into it and wondering why some of their servo max values were less than the min values.
I think I was asleep when I realized that I had no consistent axis definition. I had mentally defined Z to be up and X to be around the base and Y to be toward my laptop. But I never told it that. (How could I? It wasn’t exactly specific.) And I never checked to see if my axis obeyed the right hand rule: if Z was up, and Y was toward my laptop, was X positive or negative in that third direction? As I rotated my base to a larger servo value, was this really a valid larger angle or did I need to swap min and max?
I spent some time with my hand cramped into a fixed, right-hand axis, figuring it out. And then I spent some more time with my sketchbook, considering 2D planes and looking at the servo input and output angle measurements. I had to measure the elbow joint angle is with respect to the shoulder. This was something I could see in the kinematic equations but that I hadn’t considered when defining my axis: that angle has to be relative to the fixed XYZ axis.
I had to break down the problem into pieces I could solve: multiple 2D problems. I did, and it all worked out pretty quickly once I started thinking and stopped typing numbers hoping inspiration would strike.
One funny thing before I go: when I tell it to go from one position to another, the inverse kinematics code takes the end of the claw and goes along a straight line. I told it go from position (0, 150, 70) to (150, 0, 70). If I want to do that by controlling the servos, I would set the base from angle 90 to 180, moving all of the Y position into X position. That creates an arc with the claw. However, the inverse kinematics code calculates the shortest path (line). Instead of a turn at the base, it moves the claw in a line which requires moving all the motors and looks pretty strange.