No announcement yet.

Single Light Sensor Line Following (a white paper)

  • Filter
  • Time
  • Show
Clear All
new posts

  • Single Light Sensor Line Following (a white paper)


    This is a guide meant to be read by FLL coaches/teachers (or aspirant students) to create, in plain language, some of the more robust line following techniques (such as the much-hyped PID). All of these will only use one light sensor for the actual line following. I am going to feature three, single light sensor line followers: a binary (on/off) controller, a P (proportional) controller, and a PID controller. There is a great resource on these already available, but I thought I might try taking a shot at making the material a bit more accessible.

    True to the spirit of FLL, I will not include explicit NXT code examples, but will try to make it so that constructing your own code after reading this will be as painless as possible (including posting pictures of code where suitable). I will however assume that you know how to make a program to display the light sensor's raw value. If you need help with this please just ask, I will be happy to help.

    Herein I am using the Smart Move (2009) mat to follow along with the guide I am writing. Because of this, I am making a few assumptions about the line we are trying to follow. Most notably, the line turns to the left. For reasons I will cover later, mount your light sensor so that it is on the inside of the curve you want to follow. Placing it in front of the drive wheel is also a good idea.


    Before we can make a full-fledged PID, we must first understand how the light sensors work, so that is where we start.

    To avoid any confusion, the light sensors detect the intensity of light. It is not a color sensor, and it probably doesn't work solely in the visible spectrum of light. This means that external lighting conditions, while a factor to be considered, do not interfere as much as you'd believe they would. In fact, all of my '09 Smart Move teams' line followers worked regardless of light, even in total darkness.

    Imagine that you point a light sensor at the sun, will it read black or white? White, of course. Now imagine that an ordinary black housefly is several feet from the light sensor, but directly between it and the sun. The light sensor is pointed straight at this black housefly, with the sun behind it. What will the light sensor read? White, in the same way that your eyes couldn't look at the sun under the same conditions without being blinded.

    What does this tell us? That the light sensor detects brightness over a (small) area. Turn the light sensor on (so that it emits light), and point it at a desk or other flat surface. Notice how it projects a few red rings? The innermost one that is clearly visible (about the half the size of a pinky nail when you hold the light sensor an inch away) is approximately the polling area.

    What this means is that, effectively, what the light sensor will detect is the average brightness in the polling area. If the polling area is half pitch black, and half pure white, then you can safely assume that the light sensor will read halfway between whatever it would in pitch black, and pure white conditions. This does not mean that the light sensor will return 50% (or 512 raw value), just halfway between those two numbers.

    So what value does your light sensor read in pitch black, or pure white? Well, those two values don't matter, but what matters is what your light sensor reads on the white of the board, and the black of the board. So let us figure out what those values are!

    But first a word on set up. Because your light sensor works in an area (a cross section of a cone of emitted light) if you change how far your light sensor is from the board, it can have nasty consequences. In my experiences, keeping the light sensor within 2 centimeters of the board is ideal. Keep the light sensor perpendicular to the board. Changing where your light sensor is on the robot will result in a need to recalibrate so what I am saying is calibrate your robot in the same configuration you will run it in. Things like attachments typically shouldn't matter, though.

    Create a program that polls the light sensor continually, and prints it's raw value to the display (make sure you tell it to clear the display, else wise you might write down the wrong values). We are using the uncalibrated, raw value of the light sensor because it is the most accurate representation of how the light sensor perceives the world.

    Now take your robot, with the program running, to the board and place it over a wide open, white area. Write this value down, and be sure to label it WHITE. Then place the robot so the light sensor is directly over the blackest part of the line you want to follow (this is where its handy to have a light sensor near the ground, so the sensor doesn't accidentally get some white inside its polling area). Record the number and label it BLACK. When doing this, be sure you aren't touching the robot, or blocking its light with your shadow, etc – keep it as close to competition conditions as you know how.

    On my table, I got 444 as the WHITE value, and 666 as the BLACK value. (yes, actual values) Perhaps counter intuitively the value of the black line is larger than the white background. You may notice that even when the environment was unchanging, your values were not perfectly constant, and instead variable within a small range. This is normal behavior, and it shouldn't really matter what number you choose within that range.

    Another important concept is that our line follower will not be following a line. Instead, it will be following the EDGE of the line. This is because the light sensor only knows what its looking at, but by looking at the edge of the line we will get a value between WHITE and BLACK that will let us infer a second piece of information, how far we are from where we want to be. Trying to be on pure black all the time doesnt work because what happens when you go to white – should you turn left or right? Depends on if you overshot too much to the left or right... but the robot doesnt know which its done.
    Programming videos for FLL. (I don't like the videos that much and will be replacing them soon-ish)

    Easy data-logging and graphing for NXT-G.

  • #2
    Re: Single Light Sensor Line Following (a white paper)


    Example video.

    We now have enough information for me to start to tell you how to make the first (binary controller):

    The binary controller is the simplest type of effective, single light sensor line follower. It simply bounces off the line before going forward (you can think of it as carving out an interior polygon to the curve). To do this we are going to want to be making a decision over and over again. This means we are going to want a switch statement inside of a loop statement. For now, just make the loop infinite, we'll get to exit conditions, how to make it stop, a bit later.

    The switch statement is going to be based off of the light sensor (dont set it to light sensor though – youll see, keep reading). It is either going to be bright enough (a low enough value) to continue going forward, or too dark (a high enough value), causing it to have to turn. Our next task is deciding what is dark enough (a high enough value) for us to turn. Halfway between our WHITE and BLACK values is our best bet for this breakpoint value (you never want to use a value near your WHITE or BLACK values – because the light sensor isnt perfect, you might, for example, skip over the line if you were to set your breakpoint too near BLACK). You should know how to find the midpoint, sum the two values then half the remainder. (666+444) / 2 = 555.

    Inside the loop, but before we make our decision (before our switch block) we need to compare (read: use a compare block) the light sensors raw value and the breakpoint value of 555. Which comparison you pick (less than, greater than) is up to you, but you will need to switch the inside of your switch block accordingly. The result of the comparison we will connect with a data wire to our earlier-mentioned switch block. This means the switch block will need to take a value instead of being dependent upon a sensor directly (since NXT doesnt let you access raw value inside the switch block).

    When you are brighter than (less than) the breakpoint value, go straight (with perhaps a slight curve into the line). When you are darker than (greater than) the breakpoint value, turn away from the line. Set both motors to unlimited, so the loop is immediately run again, so it will respond as rapidly as possible. It also helps to keep from turning on a dime (reversing the interior motor) since this will make your robot jerk. I find that just a little notch from all the way on your motor (or minimotor) block is ideal.

    A working example of this is available here:

    Note, if you are using the mini-move block steering-slider has different behavior than the normal move block. It may be apt to use two mini-motor blocks (this is the approach I favor). Example here: You can set the motor that isnt moving to break or coast. Break is able to handle tighter turns (and similar benefits), but coast looks much smoother.

    What you see here is actually not ideal. The B value in the compare block is hard coded. In traditional computer programming hard coding is Bad. What if you arrived at the competition and the light values were different? Or if you wanted to reuse to code to do something else in a completely different environment? Would you go and find the midpoint each time? That might work on something so simple, but as you get to more nuanced code, like the P and PID controllers, you will start to see how reacting to one little change becomes increasingly more difficult with the scope of your code.

    Also, reusing code through the use of MyBlocks is a big thing with the judges (or so I hear), and rightfully so. Currently that program can follow one type of line – a left curving line... it cant even follow the same line back to base! We will address both of these issues after resolving something more... necessary, stopping.

    Right now the decision to move straight or turn left is in a loop that never ends. We will need to stop eventually, so let us change the loop so that its control is set from Forever to Logic. We can then supply it a boolean (anything that is either true or false is boolean, and the fancy “real programmers” like to call them 'bools') that will decide if we need to keep going, or to stop.

    Of course, we will need to know when to stop. It is plain to see that time is a poor choice – it is not sensor-driven, so it is very vulnerable to errors. We could be very fancy and remember/sum how far our motor has turned, to determine a distance traveled, but that is still reliant upon you placing the robot in the same spot on the board or your motor not failing. The best solution for determining how far to go is to use the board to tell you when to stop.

    On the Smart Move board, there are three black lines coming off of the one you want to follow. It is important to note that they all meet the curved line at its outside, while you are only using the inside to line follow. What you can do is rig a second light sensor to count the lines. You will need to use a variable to do this. Variables are how computers remember things, and are basically the same thing you deal with in math class when trying to solve for “x” (that is, if you've taken algebra/prealgebra). I wont go into how to use variables here, unless requested (and feel free to do so if you need the help).

    But first consider this: how fast are computers? Really, really fast, right? They update many times per second. If you just had three “wait for black” light sensor blocks in a row, the computer would fall through all three instantly upon encountering black (instead of waiting for 3 black lines, like you wanted). So what you should do is make a boolean variable called, say, Continue and make it “true”. Then you need to have three pairs of blocks (six total) “wait for black, wait for white”, and then set Continue from “true” to “false”. (Actually, you only need 5 such blocks – the last “wait for white” isn't strictly needed. Just remember to stay consistent in your implementation.)

    However, all of this code would lock up your line follower if put inside the same loop (since you are telling it to wait)! But thankfully NXT allows you to run threads of programming side by side (actually, it switches rapidly back and forth between them). This is what I mean:

    Note that in that picture I've changed a few things. Motor blocks instead of Move blocks (personal preference) and waiting for white/black instead of black/white. The first wait block should fall through (stop waiting) instantly because the robot should start on white, but it is still there to preserve the paired nature of the second rail (that is, so it is easier for a human to read). Don't forget to use the proper light sensor, and initialize the variable Continue on the same rail as the line follow loop (the reason for this is a bit too tangential for this document but, as always, ask and I will answer).

    All of the above looks like it should work, and it very well may, but it has a bug hidden in it waiting to bite! To see what I mean make a simple program that can display in real time the values being returned by the light sensor. Set the robot down, and watch the display. The numbers are not perfectly steady! They will fluctuate a bit, and this can be dangerous if you are simply telling your program to wait for light>50% and light<50% over and over, because the light sensor may return 49% and 51% then 49% without ever moving – counting as at least a whole line. To fix this, have your light sensors wait for, as an example, <45% and >55% (numbers were picked out of thin air).

    A nice addition to the above is to have your robot tell you when it counts a line. This and other things will be covered in the advanced programming techniques section, but for now I feel it is best to progress into discussing the proportional controller.

    P Controller

    I am not done writing this section. Will update it soon. Be aware that practically speaking, the binary controller is all you need and all the rest is mostly for showcasing your programming skills.
    Last edited by SciQuest; 06-28-2010, 06:44 PM.
    Programming videos for FLL. (I don't like the videos that much and will be replacing them soon-ish)

    Easy data-logging and graphing for NXT-G.