Kevin Walchko
created 17 Nov 2017
Ok, so we have our forward/inverse kinematics defined. We now need to send commands to the robot arm to make it move. The lab will cover this, but this is just a little more information on: servos, angles to PWM widths, and coordinate frame alignments.
In [2]:
%matplotlib inline
from __future__ import division
from __future__ import print_function
import numpy as np
from matplotlib import pyplot as plt
The first thing you need to understand are the RC servos we are using are made by the millions at the lowest price point possible. Therefore, there are little differences between every servo and there is no standard input/output relationship between servos. Typically you need to calibrate your servos and determine the max/min PWM pulse needed to move your servo between 0 degrees and 180 degrees. Note, some servos turn more than 180 degrees and some turn less ... it just depends.
You should already be familar with a servo from ECE382. However, a servo allows you to command a motor to turn to an angle based off internal feedback from a potentiometer. This is nice, because it does not require you to develop a control system with a feedback loop.
But how does it work?
Again, these are not really standardized and every manufacturer does things a little different, but ball park PWM values are:
For our robot arm, we do not need to directly talk to the servo. Instead we need to send commands to a servo driver board, which will drive the servo to the correct angle and provide power/ground. Now, our software we have developed in the homeworks will generate a joint angle (in degrees or radians), but we need to turn that into a PWM signal ... how?
Our servo driver board has a serial interface and expects a command in simple ascii text with the following format: #0P500
where
#
is the message start character0
is refering to servo number 0, but can be 0-5P800
is the command to position the servo at location 800, which could be between 800 and 2300.Now the standard PWM counts for our servo driver board is:
Angle | PWM | Gripper | PWM |
---|---|---|---|
0 | 800 | Open | 800 |
180 | 2300 | Closed | 2300 |
So if I wanted to position servo 3 at 90 degrees, I would want to send #3P1550
(1550 is half way between 800 and 2300) or I wanted to command the gripper to close #4P2300
.
NOTE: For the lab you will need to develop a function (angle2pwm(angle)
=> PWM) which will take in an angle (in degrees or radians) and return the PWM width. Also, when you develop this function, you need to be able to adjust this range. The first part of the lab is calibration, where you will double check what PWM widths work best for your servos.
Now we developed our forward/inverse kinematics using Craig's process, based on DH, but that assumes a certian orientation for how we measure angles. DH defines a rotation angle (how we want our servos to move) and the angle between $X_{i-1}$ and $X_{i}$. You need to be aware and compensate for when the real world servo orientation is different than our mathematical DH process.
Shown below are the angles for a servo on the left and the DH reference frame on the right. Notice they don't agree on the location of 0 degrees.
Here the problem is, your code (according to DH) tells you to move the servo to 0 degrees. Looking at the picture on the right, that means you want to be aligned with the x-axis (according to DH). However, sending a PWM signal to the servo for 0 degrees will rotate the servo so it is aligned with the y-axis (according to DH). You need to insert a conversion factor before you send the command to the servo.
So something like this:
# I want servo0 to go to 0 degrees in DH
offset0 = pi/2 # the frames are 90 deg off
pwm = angle2PWM(angle0 + offset0) # the servo will now align to your DH axes
When you command the serovo to 0 degrees, it will go where you want. In your lab, you will have to determine good values for your servo's PWM values, you also need to understand where 0 and 180 degrees are so you can make corrections if needed.
We will talk to the robot arm's servo controller using a Universal Asynchronous Receiver-Transmitter (UART) serial interface. These are very common and the microcontroller in the servo controller has one built in (so does the MSP430). Here we are using a USB-to-serial converter for our laptop.
The older model arms use the above, while the newer arms have the USB-to-serial built right on the board so all you need is a USB cable. The lab has the setup instructions so I won't repeat them here, but to write serial communications code in python, all you need to do is:
import pyserial
# let's create a serial port object
# the port (COM5) will be different on your laptop
# we set the datarate to 115200 for the arm, other applications could be different
ser = pyserial.Serial('COM5', 115200)
ser.write('hello') # send some data, a string saying hello
# we won't need these for the lab, but I want to show you they exist
ans = ser.read() # read in 1 byte
ans = ser.read(100) # read in 100 bytes
ser.timeout = 1 # set timeout (how long to wait for data) in seconds
# readline() needs this set to work
ans = ser.readline() # read in data that ends in \n or it will return when it times out
ser.close() # all done, close the port
For the lab, all you need to be able to do is open, write, and close the serial port. See the pyserial
documentation if you want to do more for another class.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.