8 Mission Mapping
8 Mission Mapping
8 Mission Mapping
In this lab, you will learn more about the Adafruit motor shield and how it can be used to control a stepper motor. Your work with the IR sensors will also continue as you teach your rover how to map the terrain surrounding it, equipping it with sufficient knowledge to navigate around possible obstructions, and finally reach the desired plaques of interest.
Table of Contents
1 2 Reading 1.1 References Homework and Mission 2.1 Homework 2.2 Mission 2.3 Documentation Lab Checklist Parts How it Works 5.1 General 5.2 Construction 5.3 Unipolar vs. Bipolar 5.4 Movement 5.4.1 Single 5.4.2 Double 5.4.3 Interleave 5.4.4 Microstep Lab Experiments 6.1 Gyro Calibration 6.2 Horizontal Alignment of the Scan Platform 6.3 Mapping Experiment 6.3.1 Documentation Software 7.1 Gyro Calibration Application 7.2 Mapping Software Application
3 4 5
7.3
Find Objects Software Package 7.3.1 findObjects Global variables 7.3.2 Subroutine findObjects() 7.4 Verify Software Package 7.4.1 verify Global variables 7.4.2 Subroutine verify() 7.5 Utility Functions 7.5.1 Function stepTurret() 7.5.2 Function arcSine()
Reading
Your reading this week is Chapter 10 Serial Communications Sections 10.1 and 10.1.2 Serial Peripheral Interface (SPI).
Most of the material in this document on the SPI subsystem can be found in Chapter 18 The Serial Peripheral Interface (SPI) of the ATmega328P Datasheet. We will be looking at stepper motors and the SPI serial communications as they are implemented by the Arduino and the Adafruit Motor Shield. Here is the motor shield schematic http://www.ladyada.net/make/mshield/download.html and here is the pdf documenting the shield and how to control a stepper motor (pages 25 and 26) http://www.robotshop.ca/content/PDF/adafruit-motor-shield-arduino-user-guide.pdf
1.1 References
1. Atmel AVR Micrcontroller Primer:Programming and Interfacing, by Barrett, Section 6.4 DC Motor Speed and Direction Control 2. HSI Stepper Motor Theory and Stepper Motor are good recources, among many on the
web, for learning about Stepper Motors. 3. Stepper Motor Basics - Introduction to Stepper Motors. 4. Interface Electronics - Ricky's World 5. YouTube Video - Motorola Stepper Motor Control IC 6. Atmel application notes doc7672.pdf (Sensor-based Control of Three Phase Brushless DC Motors) 7. Parallax 12-Volt Unipolar Stepper Motor 8. Stepper Motor - Wikipedia 9. Identifying Stepper Motor wires 10. http://www.netrino.com/Embedded-Systems/How-To/PWM-Pulse-Width-Modulation 11. http://www.ducttapeeng.com/smd/smd11.htm - disassembly 12. http://www.stepperworld.com/Tutorials/pgBipolarTutorial.htm - sequences
2
2.2
Before your rovers can travel to points of interest (the plaques), it needs to be able to pick them out from other terrain features (walls, cabinets, desks, chairs, etc.). At the end of this lab you should have completed a... IR Mapping Experiment - the Difference Map Software with associated documentation to identify targets within the field-of-view of the rover. As a necessary precondition for completing these experiments, the scan platform and IR rangers will need to be aligned horizontally.
2.3
Documentation
Summarize your experimental results and equation(s) in the subsection named IR Sensors within the Hardware Build section of your Project Document. This subsection should include a discussion of the modifications you made to the indoor mapping experiment in order to achieve success on the test site.
Lab Checklist
The checklist is filled in by the instructor at the beginning of next weeks lab. Horizontal Alignment of the scan platform with tilt calculation IR Mapping Experiment Graphical results with comments from running makeMap program are included in the IR section of the Project Document. Target Location Algorithm added to the Software Code Section. Results from running your findObjects software to identify targets within the field-of-
Parts
Solderless breadboard
4.7 K resistor
10uF Capacitor
LED
In addition to our essentials, we will need some measuring equipment and supplies to complete this lab...
Laser Pointer
Level
Electrical Tape
You will also need your breadboard wire bundle plus half-size breadboard.
How it Works
The best discussion on how stepper motors work can be found on Wikipedia under Stepper motor. To learn how to use these in the Arduino IDE look here. Also see AdaFruit Make it Work under the Stepper section.
5.1
General
Stepper motors can be salvaged from obsolete disk drives and ink jet printers. The motors from 3.5" floppy motors are not suitable for this lab. Bipolar motors have 4 wires and unipolar motors have 5,6 or 8. Bipolar motors generally have a larger torque and smaller step angle when compared to unipolar stepper motors. You can find common phase leads by using a ohmmeter to measure resistances between leads, common phase leads will have low resistances in around 30 ohms. Stepper motors can be broken down into 3 categories. 1. Variable reluctance stepper- magnetic flux seeking the lowest reluctance path through a magnetic circuit. 2. Permanent magnet stepper- has a cylindrical permanent magnet rotor. 3. The hybrid stepper- combination of variable reluctance and permanent magnet steppers Source: http://www.stepperboard.com/MotorConnections.htm
5.2
Construction
An inductive motor like our stepper motor has two major components: a stator and a rotor. The rotor is the magnetic armature of the stepper motor and is the moving part of the stepper motor. The rotor consists of bearings, magnetic shaft and poles. This cylindrical shaft contains permanent magnets which interact with the stator. The stator is the external part of the inductive motor that does not move. The stator contains the bearing housing, the stator coils and the stator poles. (Follow this link for a disassemble). A stepper motor is a type of motor whose circular rotation of the rotor is divided into smaller discrete steps. The number of steps a stepper motor takes in order to make one full rotation of the rotor is fixed and is determined by the number of poles inside the motor. Each step turns the rotor a certain number of degrees. Smaller step angles are obtained by adding more poles on both the rotor and stator. Both unipolar and bipolar stepper motors are constructed this way while the main difference lies in
5.3
Unipolar stepper motors are designed to have a canter tap which is common to two coils working together. The positive voltage is then applied to the common tap and the nodes a and b are alternately grounded to produce changes in polarity to the magnetic field. You can check the direction of the field by using the equation B=IxR (this is greatly simplified for our purposes) where B is the magnetic field, I point in the direction of positive current flow, and R is in the direction of where you want to measure the magnetic field at. For example if 1 is positive, a is grounded, and b is open (high impedance) current flows from 1 to a in Figure 1 and the direction of the magnetic field flows out at the left end and comes in at the right end (Figure 2 is meant to clear up which way is north or south. If the magnetic field is outgoing it is south while north is on the receiving end). If the current flows from 1 to b,while a is high impedance, then the polarity is reversed. Figure 1 Figure 2
From:http://www.cs.uiowa.edu/~jones/step/types.html
From:http://en.wikipedia.org/wiki/ File:Dipole_field.svg
A bipolar motor has no center tap and the current must flow from a to b or from b to a. We must reverse the current in order to reverse the magnetic field as opposed to switching the ground. This requires a more complex design to handle and is usually left to an H-bridge to configure.
5.4
Movement
The stepping sequence of a bipolar stepping motor can be broken down into: 1. Step mode: Full step (one-phase or two-phase), Half Step (eight-phase), Micro-stepping (not used in the lab). 2. Misc factors: Power, torque, step angle and accuracy. The type of stepping sequence that is used is dependent on the type of application of the motor.
Source: Stepper World For the Adafruit Motor shield we can have four different modes of activation. These modes are Single, Double, Interleave, and Microstep. For a GIF animation showing these stepper modes visit CNC Router Source.
5.4.1 Single
This mode refers to the single-coil activation aka the Wave Drive, One-Phase activation of the motors. See GIF animation. For the ROB-09238 this gives you a step of 1.8o.
5.4.2 Double
This mode refers to the double-coil activation aka the Full Step, Two-Phase activation of the motors. See GIF animation. For the ROB-09238 this gives you a step of 1.8o.
5.4.3 Interleave
This mode refers to the single-coil/double-coil aka the Half Step activation of the motors. See GIF animation. For the ROB-09238 this gives you a step of 0.9o.
5.4.4 Microstep
The micro-stepping mode is the most complex of all the stepping modes. That is why some stepper drivers only offer full and half step modes. Micro-stepping is when the current applied to each winding is proportional to a mathematical function, providing a fraction of a full step. The most common divisions are 1/4th, 1/8th, 1/10th, etc. However, there are some drivers that provide up to 1/256th of a full step. Micro-stepping provides greater resolution and smoother motor operation. This is very advantageous as it reduces the need for mechanical gearing when trying to achieve high resolution. However, micro-stepping can affect the repeatability of the motor. (source: CNC Router Source).
Lab Experiments
To accomplish our objectives, the lab consists of a number of experiments. Do Not Forget Unless otherwise indicated, all experiments may be run with fully charged battery or from an external source (USB or Adapter). Please, include all experimental setup and results in your lab notebook.
2. Place your rover on the raised platform at the front of the lab. Using your level, find a relatively flat and level part of the floor. After placing your rover at this location, again using your level, verify that the base of the rover is level to the floor.
3. Using your triangle adjust the stepper motor mount so the shaft of the stepper motor is perpendicular to the base. This assumes that any adapters you have attached to the shaft of the stepper motor are also aligned. Verify this alignment for all 360 degrees of rotation. 4. Using the triangle and level adjust the scan platform/IR sensor so it is perpendicular to the shaft/mounting adapter of the stepper motor and parallel to the floor for all 360 degrees of rotation. It is critical that the IR sensor not be tilted with respect to the axis of rotation.
5. Have the rover run the scan platform demonstration sequence from last week. 6. Approximately locate the laser beam's maximum and minimum height from the floor. 7. Place a target at these two locations 4 meters from the rover. You may have to reorient the rover to be able to locate the targets on the platform. 8. If required, repeat steps 2 through 7, until the laser beam stays approximately level to the floor during its 360 degree scan. Once you feel the scan platform is mission ready, program the rover to move one meter and run the scan platform demonstration sequence from last week. Locate the minimum and maximum height. Do not move the scan platform by hand - we are trying to replicate the real-world that the rover will encounter during the mission. Place targets as far away as you can at these two locations and record the beam height, distance, and the rotation angle from the home position (straight ahead). Tell your rover to move approximately 1 more meter, and again have the rover run the scan platform demonstration sequence. Record beam height, distance, and the rotation angle.
Have the rover move an additional meter and repeat the sequence. After running these experiments calculate the maximum angle that the floor could be tilted, so the laser does not hit the floor or go over an 11 inch high target. Rotation Angle Test 1 Test 2 Distance to Target Beam Height Signature/Date
Using your compass, string, and ruler measure the rotation angle and distance of the targets relative to the IR sensor(s). Record the location (angle and distance)
of these plaques in your lab notebook. You may also want to take a picture(s) and/or video for future reference. As you can see in the figure below, the photo makes a nice background for displaying the results of your mapping experiments. I recommend running with the laser ON in order to record when the IR is looking at a target. Download and run the program. Launch the terminal program (warning: opening or closing the terminal program during a scan will effect your experimental results). You should see the message "in setup," followed by "press any key to start scan." Press any key to start the scan. Record in your lab notebook the steps at which the laser hits a target. After the scan platform has mapped 180 degrees, spun 360 degrees, and completed the next 180 degree scan, you should see the message "data set complete," followed by "press any key to start scan." Remove the plaques from the scan area and press any key to start the second scan. Stop the program after the scan platform has completed two full mappings as previously defined (with and without plaques). Cut and Paste your results into a text file. Import this text file (comma delimited) into an Excel spreadsheet. Add columns translating test values into distances. For both mappings, add a column named sample difference and have Excel calculate the difference in the average distance from each measurement (dn - dn-1). Next add a column named mapping difference and have Excel calculate the difference in the average distance for each sample angle (with and without plaques). You are welcome to download my Excel spreadsheet and use it as a template for recording your data.
6.3.1 Documentation
Document your experimental results and subsequent calculations in the subsection named IR Sensors within the Hardware Build section of your Project Document.
7 Software
7.1 Gyro Calibration Application 7.2 Mapping Software Application
The following program requires utility functions getRange and ReadADC located in the "Utility Functions" Section of Lab 4.
/* * * * * * * *
Mission Mapping Software Begins by... note: word is equivalent to unsigned int, which is equivalent to uint16_t By Gary Hill
* March 27, 2010 */ #include <stdint.h> #include <avr/io.h> #include <avr/pgmspace.h> /* * The range data needs to be unsigned int * * rangedata.h should look like this: * const int range_length=1024; * * const unsigned int range_data[] PROGMEM = { * 437, 437, ... * }; * */ #include <AFMotor.h> #include "rangedata.h" byte sensorPin=4; // 400 entries x 2 bytes/entry = 800 bytes, ATmega328P has 2,048 bytes of SRAM int map_data[400]; AF_Stepper motor(200, 1); // instantiate a 1.8 degrees/step stepper motor void setup() { motor.setSpeed(10); Serial.begin(9600); Serial.println("in setup"); analogReference(EXTERNAL); motor.release(); makeMap(); } void loop() { } /* * Make a Difference Map */ void makeMap() { int dn = 0; tweak(); // adjust IR Ranger zero angle // generate a difference map // where Vref is 3.3V
// 10 rpm
// take 200 measurements and 199 steps (stops at 179.1 degrees clock-wise) for (int i = 0; i < 200; i = i++) { dn = readADC(sensorPin); map_data[i] = pgm_read_word(&range_data[dn]); // Serial.println(map_data[i]);
motor.step(1, FORWARD, INTERLEAVE); // 0.9 degrees/step delay(60); // @ 10 rpm, this should take 15 ms + 45 ms margin } // rotate counter clock-wise 399 steps (stops at 180 degrees clock-wise) // 0.9 degrees/step x 399 steps = - 359.1 degrees motor.step(399, BACKWARD, INTERLEAVE); delay(10000); // @ 10 rpm, this should take 6 seconds + 4 second margin // take 200 measurements and 199 steps (stops at 359.1 degrees clock-wise) for (int i = 0; i < 200; i = i++) { dn = readADC(sensorPin); map_data[i + 200] = pgm_read_word(&range_data[dn]); // Serial.println(map_data[i + 200]); motor.step(1, FORWARD, INTERLEAVE); // 0.9 degrees/step delay(60); // @ 10 rpm, this should take 15 ms + 45 ms margin } // cancel extra step motor.step(1, BACKWARD, INTERLEAVE); Serial.println("initial mapping complete"); Serial.println("add targets and enter go"); tweak(); // adjust IR Ranger zero angle
// take 200 measurements and 199 steps (stops at 179.1 degrees clock-wise) for (int i = 0; i < 200; i = i++) { dn = readADC(sensorPin); map_data[i] = map_data[i] - pgm_read_word(&range_data[dn]); Serial.println(map_data[i]); motor.step(1, FORWARD, INTERLEAVE); // 0.9 degrees/step delay(60); // @ 10 rpm, this should take 15 ms + 45 ms margin } // rotate counter clock-wise 399 steps (stops at 180 degrees clock-wise) // 0.9 degrees/step x 399 steps = - 359.1 degrees motor.step(399, BACKWARD, INTERLEAVE); delay(10000); // @ 10 rpm, this should take 6 seconds + 4 second margin // take 200 measurements and 199 steps (stops at 359.1 degrees clock-wise) for (int i = 0; i < 200; i = i++) { dn = readADC(sensorPin); map_data[i + 200] = map_data[i + 200] - pgm_read_word(&range_data[dn]); Serial.println(map_data[i + 200]); motor.step(1, FORWARD, INTERLEAVE); // 0.9 degrees/step delay(60); // @ 10 rpm, this should take 15 ms + 45 ms margin } // cancel extra step motor.step(1, BACKWARD, INTERLEAVE); Serial.println("difference mapping complete"); }
/* * Terminal adjust of starting angle * Enter the plus or minus character (+/-) and the number of steps (n = 1 to 9). */ void tweak(){ char ch; // used by tweak Serial.println("enter +n, -n, or go where n = 1-9 steps and go = start scan"); while(1) { while (Serial.available() <= 1) {} // do nothing // send data only when you receive data: if (Serial.available() > 1) { // read the incoming byte: ch = Serial.read(); // Serial.println(ch, HEX); if ( ch == 0x2d ) { Serial.print("minus "); ch = Serial.read(); ch = ch - 0x30; Serial.println(ch, HEX); // 0.9 degrees/step x 399 steps = - 359.1 degrees motor.step(ch, BACKWARD, INTERLEAVE); } else if ( ch == 0x2b ) { Serial.print("plus "); ch = Serial.read(); ch = ch - 0x30; Serial.println(ch, HEX); motor.step(ch, FORWARD, INTERLEAVE); // 0.9 degrees/step } else if (ch == 0x67 ) { ch = Serial.read(); delay(250); Serial.println("scanning..."); return; } else { ch = Serial.read(); Serial.println("unknown entry"); } } } }
7.3
The following program requires utility functions getRange and ReadADC located in the shared "Utility Functions" Section of this document.
// initializing the array length #define map_length 400 #define plaque_array_size 20 // findObjects variables int numPlaques=6; int sensitivity=15; // difference in distance (cm) of mapping data for a // plaque to be suspected int minWidth=2;
This software program assumes that makeMap has been called and has generated a 400 entry array.
// 400 entries x 2 bytes/entry = 800 bytes, ATmega328P has 2,048 bytes of SRAM // and 1,024 bytes of EEPROM int map_data[map_length];
If you simply want to test findObjects add the following array declaration.
int map_data[]= {-8,14,18,7,18,85,147,200,197,174,55,0,-54,1,-14,10,6,-1,4,6,10,36,49,66,58,135,-110,-3,0,10,1,-1,6,2,2,1,18,53,50,45,43,39,25,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,18,17,20,19,16,16,16,9,13,-3,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,-1,0,0,-1,0,0,0,0,-1,0,0,3,0,31,79,97,100,103,111,118,122,131,137,140,34,7,5,-3,-10,14,-9,7,9,15,0,0,0,0,0,0,0,0,0,0,0,17,42,52,-10,15,25,3,46,12,87,179,224,288,76,108,125,32,0,-1,-7,-12,0,7,-9,-5,0,-11,12,-10,-3,4,1,-2,-14,6,2,12,30,40,-27,5,23,-19,6,5,-15,-46,-17,-97,23,93,32,101,21,3,0,0,0,0,0,0,0,-4,-1,3,29,52,-2,-13,0,83,0,0,0,-2,0,0,0,12,94,-143,-309,-71,-69,-27,37,139,265,148,4,77,197,0,1,0,264,72,0,77,135,243,0,15,11,16,2,-242,320,-205,1,5,8,10,0,-1,-1,0,0,-8,15,9,0,-16,-292,-125,-239,-8,-11,2,0,0,-1,0,40,0,0,-1,82,0,76,206,4,0,2,9,0,0,-2,4,0,-9,6,4,4,-4,1,0,0,0,0,0,0,0,0,0,0,0,0,-20,0,6,2,1,1,0,-7,2,0,1,1,-5,-2,-1,10,14,6,-2,12,7,5,-4,1,1,-16,14,16,0,0,15,8,0,-7,21,0,0,2,0,0,0,0,0,0,0,1,2,-1};
// if the difference is greater than (15), and we're not already recording, // then start recording plaque length if((map_data[i]>=sensitivity)&!plaqueDetected){ beginning = i; // The previous gap count could be moved in code for different effects. // It is placed here so that it counts the total length of gaps in a // signal as opposed to any individual gap in a signal ie: for a // tolerance of 2 10101011 won't pass. gapCounter = 0; plaqueDetected=true; } if((map_data[i]<sensitivity)&plaqueDetected){ gapCounter++; // if the value falls below (15) dont count ending = i - gapCounter; // the extra gap in the ending of the plaque if(gapCounter > gapTolerance) { // only stop after the gap has exceeded plaqueDetected=false; // the tolerance if(ending-beginning >= minWidth){ // if plaque was wide enough count it p_angle[p_Idx]= (ending + beginning)/2; // save the angle as the //Serial.print(p_Idx); // midpoint of the beginning and end //Serial.print(": "); //Serial.println(p_angle[p_Idx]); p_Idx++; // move to the next array location } } } } verify(p_Idx); // manual override y/n of detected plaques
} // else gapCounter=0; // place the reset to gapCounter here if you'd like // to clear it after the end of every gap
7.4
Needs to be modified to write final list to EEPROM The verify function allows the user to approve/disapprove plaques found by findObjects. It also recovers distance-to-plaque data destroyed by makeMap in order to reduce SRAM overhead.
// 12 *2 = 24 bytes
int p_length = 0; // number of verified plaques, may be less than // actual array size if some plaques were not approved
7.4.2
Subroutine verify()
void verify(int numDetected){ char userIn; int angle; // current angle to detected but not verified plaque int dist; // current angle to detected but not verified plaque boolean valid; // used to see if user input is valid Serial.println("Please manually verify with a y/n if this is a plaque"); Serial.print("This is out of: "); Serial.println(numDetected); Serial.println("Angle:Distance"); p_length = -1; // initialize number of verified plaques
for (int p_Idx = 0; p_Idx < numDetected; p_Idx++){ valid=false; // angle is a interleave number of steps NOT degrees or radians angle = p_angle[p_Idx]; // This assumes FORWARD is CW and BACKWARD is CCW // This may not be a good assumption for your rover. motor.step(angle,FORWARD,INTERLEAVE); delay(60); dist = getRange('L'); Serial.print(angle); Serial.print(": "); Serial.println(dist); Serial.flush(); while(Serial.available()==0){} valid = false; while(!valid){ userIn=Serial.read(); if (userIn=='y'||userIn=='Y'){ p_length++; p_dist[p_length] = dist; p_angle[p_length] = angle; valid=true; } else if(userIn=='n'||userIn=='N'){ valid=true; } else { Serial.println("Invalid input"); Serial.flush(); }
// now that the user has verified or disallowed this plaque // return to home position. motor.step(angle,BACKWARD,INTERLEAVE); } } Serial.println("Done"); }
7.5.2
Function arcSine()
/* * Find offset angle (in interleave steps) based on distance-to-target * ref: http://www.nongnu.org/avr-libc/user-manual/ group__avr__math.html#g98384ad60834911ec93ac5ae1af4cf0a * asin argument must be between -1 and 1, returned value is between * -pi and pi (-90 and 90 degrees) * * INPUT * Data Argument Description
* Type Units * double a_opp cm opposide side of a right triangle * (typically offset distance) * double a_hyp cm hypotenuse of a right triangle * (typically dstance measured by ir ranger) * * OUTPUT * returns angle steptype * * note: if steptype is INTERLEAVE then a step is (0.9 degrees/step), * otherwise SINGLE is assumed (1.8 degrees/step). */ int arcSine(double a_opp, double a_hyp){ double d_x; float d_angle; d_x = a_opp/a_hyp; if (-1 > d_x || d_x > 1) { // domain error digitalWrite(ledPin, HIGH); Serial.print("domain error x = "); Serial.println(d_x); return(-1); } d_angle = asin(d_x)/radians_step; // calculate angle in degrees return int(d_angle); } // convert to steps
7.6
EEPROM Functions
EEPROM.write(address, value)
Parameters address: the location to write to, starting from 0 (int) value: the value to write, from 0 to 255 (byte) Example
#include <EEPROM.h> void setup() { for (int i = 0; i < 512; i++) EEPROM.write(i, i); } void loop() { }
EEPROM.read(address)
Parameters address: the location to read from, starting from 0 (int) Returns the value stored in that location (byte) Example
#include <EEPROM.h> int a = 0; int value; void setup() { Serial.begin(9600); } void loop() { value = EEPROM.read(a); Serial.print(a); Serial.print("\t"); Serial.print(value); Serial.println(); a = a + 1; if (a == 512) a = 0; delay(500); }
//Extension to EEPROM library by Tim Hirzel #include<avr/EEPROM.h> float readFloat(int address){ float out; eeprom_read_block((void *) &out, (unsigned char *) address , 4 ); return out; } void writeFloat(float value, int address){ eeprom_write_block((void *) &value, (unsigned char *) address, 4); }