E15B Laboratory 2

Your Task

Your task this week is to build an "etch-a-sketch" like device that has two knobs. One knob moves the laser in the "y" direction, and onther moves the laser in the "z" direction. Unlike most other labs, you wIll be given very little information about how to do this - it is more like an well-defined independent project. This document will take you through some exercises to get you started, after that you must figure out how to proceed.

To turn in

On moodle turn in your well commented code, including the code for gotoAngles(). Reminder: your code should be written so that when the potentiometers are at their midpoints, that the apparauts is set to y=0, z=0. Also include a quantitative measure that your apparatus works (though it may not be very accurate). Some graph paper is available if you want to measure actual laser motion vs predicted motion.

Your board:

Below is a simplified schematic of the board you built in lab last time. A pdf version is here.

To do this week

The pots

To familiarize yourself with the device you will be using run the following code. As you turn the two potentiometers you should see the value returned by the "analogRead()" function vary from 0 to 1023 on the serial monitor.

A2D_1.ino

Scaling

We want the potentiometer output to represent an angle from 0 to 180 degrees as the pot turns counterclockwise. To do this, we can use the map command. For example the code

ang=map(pot,0,1023,0,180);

maps the range 0 to 1023 to the range 0 to 180. If

A2D_scale.ino

Another scaling

If we want to map one of the pots to go in the opposite direction (i.e., a counterclockwise turn makes the angle go from 180 down to 0). We can simply change the mapping function.

A2D_scale2.ino

Hooking up the motors.

The motors take more power than can be supplied by the USB conntection, so connect a 6 to 8 volt DC supply to the power jack on your board.

The following code creates two "Servo" objects (Servo myServo1, myServo2;), then intializes them (myServo1.attach(SRVO1);), and finally sets the shaft angle (myServo1.write(pot1Ang);). Attach the "shoulder" servo (φ) to JS1. Note that the Black, Yellow and Red wire positions are labeled on the PCB as "B Y R". Attach the "wrist" servo (θ) to JS2 (myServo2). Note that the angle for one potentiometer is the opposite from the other - this is just to show you that it can be done both ways. I intentionally did not pay attention to which direction the motors moved when potentiometers were turned (so you could figure it out yourself).

PolarSketch.ino

A Helper Function

Write a function "gotoAngles()"

/* This code makes the motors go to angle phi, theta
as defined by the geometry of the problem.
When phi is zero, the arm should be parallel to the wall.
As phi increases the shoulder should turn counter clockwise
   (as seen from above).
When theta is zero, the laser should be parallel to the ground.
As theta increases the laser should point higher and higher. */

void gotoAngles(int phi, int theta) {
   ... add your code here
}

When phi=0 the arm should be parallel to the wall, and move counterclockwise (as seen from the top) as phi increases.

When theta=0 the arm should be parallel to the tabletop and move the laser upward as theta increases.

You'll have to offset the angle so that the zero is in the right spot (each laser pointer is slightly different, but numbered so you can find the same one later). You can assume that incremental motion is accurate (i.e., moving from 32° to 47° will move 15°). Don't adjust/dismantle the mechanism to try to set the zero angle, do it with software.

Test the code to make sure it works with the following code as your "loop()" function. If you ask me for help, I'm going to ask you to show me this code working before we go on.

void loop() {
   gotoAngles(0,0);     % move to 0,0.
   delay(2000);         % long delay.
   gotoAngles(45,0);    % move counterclockwise.
   delay(1000);         % 
   gotoAngles(45,45);   % move up.
   delay(500);
}

If you are unsure how to proceed, just change your "gotoAngles()" function to:

void gotoAngles(int phi, int theta) {
   myServo1.write(phi);
   myServo2.write(theta);
}

This code will not work properly, but will get the motors moving. You will need to figure out how to get the motors to be in the proper position when (φ,θ)=(0°,0°), when (φ,θ)=(45°,0°), and when (φ,θ)=(45°,45°).

Your task

Use this information to write a program that uses the two potentiometers as indicators of y and z position (instead of θ and φ). Use these values to calculate and set the motor angles, θ and φ. When the potentiometer are near the midpoint (i.e., they read a value of 512) the angles should be set such that y=0, z=0. Think about how to proceed before starting and discuss with lab partners to make sure everybody is on the same page. It should not take a lot of code, but it must be well thought out.

Proposed Practices / Potential Pitfalls

If you do your own inverse kinematics and need the arctangent function, you should know that there are two functions to calculate the arctangent, atan() and atan2(). You should use atan2(). To see why, consider a point in the first quandrant, (x0,y0)=(3,6).

The slope of the line is tan(θ) = y0/x0. The angle θ can be calculated as

θ = atan(6.0/3.0) · 180/π, so θ=63.4°, or
θ = atan2(6.0,3.0) · 180/π, and θ=63.4°

As expected, we get the same answer in both cases. Now consider a point in the fourth quadrant, (x0,y0)=(-3,-6).

θ = atan(-6.0,-3.0) · 180/π, with θ=63.4° (incorrect), or
θ = atan2(-6.0,-3.0) · 180/π, and θ=-116.6° (correct).

Clearly the second answer is prefered. Unless you have a reason not to use it, atan2() is generally a better choice than atan().


Good luck and bonne chance!