Technology/Engineering/Robotics
978-448-6362
Groton-Dunstable Regional High School Groton, MA
Vision recognition can be very complex. If you’ve already started this project you may have come to a few conclusions:
The reason for this is actually quite simple. Gravity. It’s constantly pulling the arm down. Also, you’re only providing it with a binary choice of raising or lowering to reach a target value. You need to be able to do a few things to do this gracefully. First, you need to create a “dead zone” where the arm won’t try to go up or down. This is your “good enough zone.” Second, to make it really smooth, you’ll need to account for the speed of the arm as well as the natural variations in the reading of positions (which can vary enough to cause issues.) This is where it gets complicated. We need to create a control system that can account for all of these variables. In the industry, this is called PID Control.
From Wikipedia: A proportional–integral–derivative controller (PID controller or three-term controller) is a feedback-based control loop mechanism commonly used to manage machines and processes that require continuous control and automatic adjustment. It is typically used in industrial control systems and various other applications where constant control through modulation is necessary without human intervention. The PID controller automatically compares the desired target value (setpoint or SP) with the actual value of the system (process variable or PV). The difference between these two values is called the error value, denoted as e(t). It then applies corrective actions automatically to bring the PV to the same value as the SP using three methods: The proportional (P) component responds to the current error value by producing an output that is directly proportional to the magnitude of the error. This provides immediate correction based on how far the system is from the desired setpoint. The integral (I) component, in turn, considers the cumulative sum of past errors to address any residual steady-state errors that persist over time, eliminating lingering discrepancies. Lastly, the derivative (D) component predicts future error by assessing the rate of change of the error, which helps to mitigate overshoot and enhance system stability, particularly when the system undergoes rapid changes.
I don’t expect you to be able to do this on your own. But with a little help and guidance you’d be surprised what you can learn! Let’s look at some code with explanations.
First, we need to organize our program based on the things we know we need it to do. To make things easier to read (and write) I’m going to start off by naming different states the robot could be in. To do this, we are using a command called “enum”. It stands for enumeration and it basically allows us to name integers. For example, if you wanted to create a “state” for something that is either on or off, you may simply create an integer and set it to 0 to represent off and 1 to represent on. It’s pretty simple and it’s normally what I would do. However, with multiple states in a lengthy program such as this, it’s helpful to assign names to these numbers.
At the bottom of the code snippet below you should be able to identify where I’ve initialized the state as “TRACKING”. Notice that “state” is in yellow. This is a recognized command by VEXcode. “RobotState” is what I named it with the enum command.
Hint: Click image to open it full size in a new tab.

You’ll also notice the “const char* stateNames[ ]” command below the enum section. This will give us the ability to display the enumerated state names on the screen for easy reading later on. We will call it using the following command: Brain.Screen.print(“State: %s”,stateNames[state]); where %s is a string variable flag and “stateNames[state]” will fill in the actual state name.
Next, we need to create a number of variables that will be used for the process. Some you already know, others, probably not:

Next, we need to create a subtask that will control the arm. I’m putting it in a subtask because I want it to always be running in the background while the drive motors have the ability to move and change directions simultaneously with the arm. The following code should be run in a while(true) loop inside an int armMovement( ){… structure.

Next we’ll look at how I controlled my drive wheels. This is comparatively simple to do. I’m doing two things here. One, I’m simply telling it to turn to follow the ball when it moves to either side of the center of the camera (field of view). Two, through trial and error, I’ve determined the tracking works best if the ball is not directly in front of the camera. To ensure the robot has the best perspective of the ball I’ve instructed it to use the distance sensor to back away from the ball if it’s too close.

Now we’ll create a simple function to grab the ball. This makes it easy to call when needed and it will keep the main task a bit neater.

Finally, we’ll look at the main task. You’ll notice a few things at the start. I’ve created a graphic display that tells me important information such as distance, battery level, Y offset and Y target, the current state, and for good measure, a bar graph comparing the raw input data to the filtered input. I’m also activating my emergency stop command and resetting my arm and claw. I’m then opening the claw to create a clear view for both the camera and distance sensor. I’m then starting my armMovement subtask. Read through the while(true) loop and see if you can follow my logic.



Hopefully you’ve followed the logic and now realize the purpose of PREDATOR_MODE is to cause the robot to stop if it loses sight of the ball while in APPROACHING mode. In this current configuration, you would need to move the ball back in front of the robot for it to continue. A much better solution would be to allow it to continue moving left or right to “scan” to reacquire the object and continue approaching.
Finally, I added a button to end the program. It breaks from the running loop, stops the subtask, stops the motors, resets the arm and claw, and finally exits. Since VEX thoughtfully stops all motors when exiting a program, I did not need to write code to stop the running subtask from moving the arm, but it’s good practice to do it anyway!
Since I’m a super nice guy, I’m making the zipped project available to you for easier reference. Please don’t just copy it, I want you to LEARN from it! But, you can use parts of the code to make your life easier. After all, the best way to learn how to code is to look up what other people did to accomplish your current goal.