February 8, 2014

Robot Arm, Now With The Drawing Ability Of A 5-Year-Old

Last update I was bad at serial and the robot arm almost exploded itself.  I quickly made my serial communication less stupid, and added some features like a checksum, which stopped the robot from behaving so spastically.  However, no matter what I did I was unable to get smooth motion while streaming serial commands.  The robot would follow the path smoothly for a few seconds at a time, but would be interrupted by jerky motions I could not diagnose.  One strategy for fixing this might be buffering the serial commands rather than executing them as they arrive.  For now though, my Python script simply generates a text file with all the commands, rather than streaming them over serial.  I can then copy the text file to the mbed for it to execute.  While more time consuming to change paths, this method has been much more (read: completely) reliable.

So here's the robot drawing some things:

Fast squares:



Writing "MITERS":



Here are some mediocre squares.  I've gotten it to draw better ones (without weird corners) since this since, but it's actually roughly the size it's supposed to be:


This is what happens when I try to draw 10 cm squares at a few Hz..... the "circles" were also drawn at the same frequency.  There are a couple reasons why it looks to terrible.  First the robot was shaking the table it was on.  Second, the paper was held below the pen by my hands.  These two problems were responsible for probably at least 2/3 of the waviness.  The remainder was caused (I think) by flexing in the linkage between the second link of the arm and its motor.  It might not be reasonable to expect the arm to be able to draw three perfect squares per second, but I can definitely get them a lot better than this.


Here's what the "MITERS" text looks like.  Once again, I was just holding the paper below the arm with my hands.


I've started simplifying my method of path generation.  Right now, I feed my python script some setpoints.  It interpolates between these points at whatever step size I choose, converts these X-Y positions to arm joint angles through the robot's inverse kinematics function, and then turns those angles into encoder counts which are loaded onto the robot.  Eventually I want to write a G-code interpreter to make path generation easy.

On to some hardware.  I made a quick pen holder out of a botched elbow joint I machined a while ago:


I also made a quick demo-frame out of 80-20 for Techfair.


Action shot:


I am working on replacing the carbon fiber linkage assembly with something that can't twist where it connects to the pins at the pulley and arm:


Back to some robot control stuff.  In the above videos, you may notice the tones the robot generates while moving.  This tone corresponds to the frequency at which I have the robot arm read points.  Right now, the arm only has position control, so when it gets a new goal point, it attempts to travel to and stop on that point as fast as possible.  So my servos are kind of trying to be stepper motors.  To smooth out motion, I plan on implementing some velocity control on top of the position control.

At the high level, velocity control is pretty simple to explain.  When reading through the list of points to move to, the robot will look ahead a step.  By looking at the position change required between the current step and the next step, it can figure out how fast it needs to be moving when it arrives at its current step.  For example, if one of the motors has a current goal position of 100 (arbitrary units), a next goal position of 101, and the time between steps is one second, when the motor reaches position 100 it should be moving at a speed of around 1 per second.  If the current goal is 100, and the next goal is also 100, the motor should be completely stopped when it reaches position 100.  The actual velocity control will be done by a PI velocity loop running within the position control loop.

So that sounds great, but it makes a big assumption:  that you actually have a good idea how fast your motors are moving.  For my position control loop, I determine the motor velocity for the D term of the PID loop by taking the difference between consecutive position readings.  The issue with this is that encoders are digital.  When you try to sample the velocity really fast by subtracting position readings over very short time intervals, you lose resolution in your velocity measurement.  So this method is no good for running a very fast velocity control loop.  Also, I have been feeding my robot new points faster than I am able to sample for velocity, meaning that a velocity loop would be too slow to do anything.

Skimming through a bunch of papers on servo control loops, I found that my problem was not at all unique to my system.  Turns out the solution is to estimate the velocity at a high rate, and then update your estimate at a lower rate with encoder feedback.  Estimating the motor velocity will work pretty similarly to my python script to simulate the arm back at the beginning of this project.  A guess at the change in velocity over a cycle will be made by calculating the predicted acceleration of the arm given the arm's inertia, the motor's torque-speed curve, the previous command to the motors, and the loop time.  As actual measurements from the encoders are collected, the estimated velocity will be updated.  This will involve figuring out more precise values for inertia and frictional torque in the system than I've used before.  Also, the inertia of the arm's first link will be some function of the angle of the second link, which will make things a little trickier.

So there's a lot more work to be done.  At some point I need to add a third axis too....

No comments:

Post a Comment