“Quarter Twenty’s” Controls
by Ben
Newton
A review
of my contributions to the project, and some suggestions for next year’s team
As the current semester comes to an end, and I prepare to graduate from college, I thought I would write one last paper detailing my contributions and experiences with the 2002 University of Utah Walking Machine Challenge Team. I was a proud member of this team that created Quarter Twenty (or qt for short). Quarter Twenty is a walking machine (or a walking robot) that our team designed and built to take to an SAE competition in Golden Colorado in April of 2002. It is my hope that this paper will explain my involvement in, and contributions to the project. I also hope that the paper will help to next year’s team as they try to take over where we left off and make improvements to Quarter Twenty. Hopefully after reading this paper a person will have a good idea of what we accomplished on the software side of the robot, how it works, and what the next steps should be.
After finalizing the arrangements to receive independent study credit for helping with the project and consulting with Dr. Minor, I was ready to start working on the project. Unfortunately, it wasn’t until the middle of January when I finally started meeting with the team to discuss the controls and software for the robot. At this point the robot was still in a couple of pieces and it was a bit of a struggle just for me to even envision how it would walk. I met a couple of times with the group and discussed some of the ideas I had, and they helped me to understand some of the issues involved. Before we knew it, the Olympics were upon us, and the school was closed down for a month. We had high hopes of meeting during the Olympic break, but busy schedules, and lack of communication dashed those hopes.
February 27th to March 9th – Fix
computer and Install Linux & RTLinux
Soon after the break ended we had a Controls Team Meeting to discuss the overall software design. It was an interesting meeting as we had a Mechanical Engineer, an Electrical Engineer, and a Computer Scientist all trying to communicate ideas to one another. We decided that we needed a real time operating system in order to have reliable control. I was assigned the task of getting a real time operating system running on our computer. It was a daunting task, considering I had never even installed a regular linux system, and knew little about how to configure things in linux.
Well, we ran into trouble on the first step of installing linux: “Turn on the computer”. It seems that a new processor had been installed on the motherboard, which wasn’t supported by the old bios. We finally found a bios upgrade for the motherboard (which wasn’t an easy task), and then we tried installing the upgrade. We couldn’t install the new bios upgrade with the new processor installed. Unfortunately the old processor was long gone, so we ended up having to go out and buy an older processor just so that we could update the bios. We then re-install the new processor and everything seemed to run fine. This was the type of luck that followed us through the entire project
With a bootable computer we were now ready to begin installation of Linux, and then RTLinux. RTLinux is a real time kernel that sits below the regular kernel and intercepts communication between the hardware and the regular kernel. This Real Time kernel is then able to run Real Time modules with full precedence and correct timing. Unfortunately the documentation and Installation instructions for RTLinux were vague at best, and were definitely not geared towards a first time “installer” like myself. I struggled through the installation errors, and finally got RTLinux up and running.
March 10th to March 25th – DAQ wars
The next step was to get the DAQ card working on our computer. Little did we know how big of a step this would be. We had a National Instruments ATMIO ISA DAQ card that we planned to use to send data to the robot and to receive output from the robot. We couldn’t seem to get it to work. First of all, National Instruments didn’t support any Linux drivers for it’s products, but they pointed us to a package of Linux drivers called Comedi which they said should help us to get the card working in Linux. We couldn’t, however, even get linux to detect the DAQ card. We tried everything we could think of, but had no luck. We even installed Windows on the computer and it still couldn’t recognize the card. About this time I started getting depressed about the project. It seemed like we were spending so much time on it, and getting nowhere. Luckily we had a meeting and re-evaluated where we were, and what our priorities should be. The verdict was that I should just start doing some of my coding, and not worry too much about the rest of the stuff. That was great! I started writing some of the upper level event code (code to accomplish each of the events), and Michael and I started talking about how our code modules should interface.
March 26th to March 31st – Breaking
the hardware/software barrier
We still couldn’t get the DAQ card to work on the computer we had planned to put inside the robot. We finally deduced that the Motherboard and back plane wouldn’t support the ISA DAQ card. Who would have thought that would be a problem? Luckily we found out there were some Identical PCI DAQ cards available, and we got one of those cards. With the PCI card we had the board up and configured in no time at all. On March 28th we finally broke the hardware/software barrier. We created a small program that would send signals to raise and lower a single toe when we pressed enter. We hooked everything up, and it actually worked! I’ve never seen so many grown men so ecstatic to see something so simplistic work. With one month till competition we still had lots to do, but we were one step closer.
April 1st to April 6th – A step
forward and two steps backward
Me made slow progress with trying to get Comedi and RTLinux to work together this week. We could get each of them to work separately, but not together. RTLinux is inherently difficult to work with because it is so close to the hardware that any programming bugs generally cause the system to reboot.
Michael made good progress on the lower level control code this week, and we were able to get cylinders to move to a specific relative position.
We all prepared to sacrifice a Saturday and make a big push ahead during the weekend. We got the computer installed inside the robot, and finished up all the wiring to the valves on Friday night so everything would be ready on Saturday. On Saturday we all met early in the morning, and started forging ahead. We made some great progress, but also ran into some difficulties. We accidentally ran too much current into our Power inverter, and it started smoking. Smoke is generally not a good sign. I’d like to say that was the worst thing that happened during the day, but unfortunately later in the day we smelled smoke again. This time it came from the PC area of the robot. We knew that wasn’t good. Upon inspection we found that the $500 PCI DAQ card had gotten too much voltage, and one of it’s chips had “fried”. The students who had been testing some of the valves accidentally touched a hot 24 volt wire to the robot frame, and that voltage made it’s way to the DAQ card and caused the damage. Fortunately we were able to get a lot done during the day, and we made some great progress with the software side of things.
April 7th to April 11th – She walks
Luckily we were able to quickly secure a new DAQ card and power inverter, and get back on our feet. This week I did a lot of coding, and spent a lot of time trying to get everything running together. After solving some problems with shift registers and a messed up ribbon cable we were ready to get serious about making this thing walk.
On April 11th we finally decided to forget RTLinux, and just get the robot to walk. We decided that the benefits of Real Time were not worth all the hassle, especially with the time constraints. I spent all day integrating the two modules (lower level control, and upper level tasks) and writing an initialization routine. Finally, early the next Morning Quarter Twenty came alive and took “her” first steps. It was a site to behold.
April 12th to April 17th – Coding
and Testing
By this time working on the robot had become an obsession. Every waking minute that I had to spare, I spent in the robot lab typing away on the keyboard trying to get things to work and make improvements. It was difficult at times because there were still a lot of little things being done on the machine. This made it hard for me to run the tests I needed to when I needed to. Everything seemed to take so much longer than I expected it to. There were so many things that could go wrong, and they usually did.
On April 17th we were ready to hit the halls of the MEB. We walked down the hall without any wires attached and walked to within 8 inches of our 12-meter goal.
April 18th to April 24th – The last
big push
The last week was awful. There were so many things to code and test and debug, and so little time. Suffice it to say there were a lot of late nights, early mornings, bags under eyes, and empty pizza boxes involved. Rotation was one of our biggest problems and concerns. It was really hard for us to turn quickly to a specific position and then stop there. We had several different ideas, but none of them seemed to work, and we were having trouble with pressure being built up in the rotary valve and turning the machine when we wanted it to stay straight. Another problem we faced was the possibility of getting into some oscillations as we tried to get to a specific position. If left unchecked this could cause the robot to overturn, or never complete tasks.
April 25th to April 27th -
Competition
We made it to Golden Colorado Safely, and made the final preparations for competition. Because of the issues with turning, Jared and I stayed up all night writing new functions which would allow the robot to run certain events in a cross formation which would allow it to walk in any direction without turning. We also wrote some new code to help us get rid of the oscillations in normal walking motions. It turned out that we added nearly 500 lines of code the night and morning before the competition.
During the competition we were testing routines moments before we were called to the starting line. Sometimes we even ran code and events that had never been tested. We were very lucky.
One unforeseen problem was wireless network interference. During one of our events we lost the signal between the laptop and our wireless hub. This caused our code to stop running and caused the robot to do some crazy things until it could regain connection. We had several other problems, but thanks to second chances we were able to get things running the second time around on most competitions.
Overall the competition was a huge success. We completed every event that we were prepared for, and given enough time on the course we could have completed all the other events except possibly walking through the tires in the obstacle course. Out of the 28 teams that were scheduled to compete we placed 6th overall.
Basically we have a computer running SUSE Linux that exists within the robot itself. It can be powered by batteries, through a power inverter, or off of wall power. To work on the computer you can either plug in a monitor and a keyboard directly to the computer, plug in an Ethernet crossover cable or network cable into the network card, or plug in the wireless PCMCIA card and use the wireless connection. (Ethernet is probably the best option because of possible problems with wireless.) In any case you should now be able to log in to the machine in the robot.
The computer has a PCI DAQ card installed in it. This DAQ in turn plugs into a custom PCB board. This board takes the 8 Digital Inputs and Outputs that we have and uses shift registers to allow us to simulate the existence of about 32 Digital Outputs. The board also steps up the signal strength from 5V to 24V, which is enough to activate the valves. The 24 volts comes from the batteries, and can be interrupted by the stop switch.
The PCB is wired to each valve in the robot. Two valves are needed to control each cylinder. One valve exhausts air while the other allows air in producing motion. Each valve has a flow control regulator. This controls how fast air can escape from the valve when it exhausts. The setting of these flow controls is very important to ensure the robot functions properly. We have generally kept them tightened down quite low, allowing air to escape rather slowly, and making the cylinder move slowly. Moving cylinders too rapidly can cause overshoot and then over-correction making the cylinder oscillate.
Each Cylinder also has a potentiometer (pot for short) associated with it. As the Cylinder moves, the string that is wrapped around the pots causes the pots to turn proportionally. The pots are able to measure in voltage, where the cylinder is. This voltage reading is relayed by wire to the PCB and then to the DAQ where the computer is able to read the value.
The robot contains 11 cylinders that behave this way: Eight toe cylinders, one at each of the 4 corners of the two tables, Two translation cylinders, one for each table, and 1 rotational cylinder (it works almost the same way). There are two valves that are controlled differently. These include the grabber and the brake. These valves don’t have position sensor (pots), and are either all the way open, or all the way closed.
Our software is programmed in C and uses the Comedi library to communicate with the DAQ card. The software is split into two main modules: Lower Level Control Code, and Upper Level Task Functions.
The lower level control code is responsible for initializing the DAQ card, and initializing the robot so that we can get correct readings from the pots. The lower level code contains a function to control the shift registers so it can appear that we have more digital ins and outs than we really have. It is also responsible for figuring out where the cylinders are (in inches relative to their base 0 point), and then driving the cylinders toward there desired position (with a certain amount of error). Finally the lower level code is able to close up everything on exit.
The upper level code basically handles everything else. The only way it affects the robot is by setting desired position values (refPos) for each of the cylinders. It is also able to see which cylinders are at their desired positions (isThere). The upper level task code has functions to control every aspect of the machine, and all of the desired events. There are functions to control each of the toes individually, to control all of the toes at once, and to control each of the translation cylinders. By calling these functions in a certain order we are able to do more complicated tasks. These tasks include things such as walk forward one step, walk forward a certain distance, or turn a certain number of degrees. Another important function of the upper level code is a function that halts the progress of a section of code until all the cylinders are where they’re supposed to be. Without this function the robot would try to do everything simultaneously. Each of the events in the competition had it’s own upper level function, and each of those functions handled everything that needed to happen for the entire event.
The last thing that is controlled by the upper level code is input and output. The output code prints a display to the screen showing information about each of the cylinders and any errors. A separate custom set of functions allow for semi real-time input from the user.
This should give you at least a little understanding of how everything works. Next I will delve deeper into the code and explain more detailed how everything works.
Here I will attempt to explain the code in more detail. I’m sure I’ll miss some things, but this should give anyone a good start at deciphering our code.
First of all, the source code for controlling the robot contains 7 files:
Lets start with a description of the lower level code. Control.c contains the following functions:
Moving on to qt.c and the upper level task functions we have:
· main() – the main routine where execution starts. It initializes the DAQ, calls either long or short initialize, and then it gets the robot to a starting position. Finally the main function goes into a loop keeping things where they are, and waiting for a command from the user. After the user quits, the program, main shuts everything down, and exits.
· Finish() – moves the robot to an ending position.
· RotationFinish() – moves rotation to an ending position.
· Start() – moves the robot to a starting pose.
· GoHome() – centers the tables and retracts all toes. This is the home position.
· Center() – centers the tables.
· RotationStart() –rotates to the center angle. – because of initialization problems sometimes the center angle is wrong.
· FullStepForward()/FullStepBackward()/LazyFullStepForward/LazyFullStepBackward – all of these functions are closely related, and could be combined into one function with parameters for lazy and backward. Basically it assumes it is starting with the tables centered. It moves each table the correct direction to move forward or backward. Then it swaps legs and moves the tables back to their center position. This is considered on step. After one step the center of the robot has move about 22 inches.
· CrossOuterFullStepForward()/CrossOuterFullStepBackward()/CrossInnerFullStepForward()/CrossInnerFullStepBackward() – These functions are functions used to walk when we are in the cross formation. Where the tables are turned perpendicular to each other. In this position the outer table can’t translate as far as it usually does, so we must stop it 2.5 inches before it’s max and min positions. Basically these functions work much the same way as the Full Step functions, except that we are only moving one table, and when we call the Inner Cross functions we are really moving left and right.
· LazyDoubleStepForward()/LazyDoubleStepBackward() – these functions as well as the cross functions above were written at competition to overcome some obstacles. These double functions get rid of any oscillations if we are just walking forward. Unlike FullStepForward type functions, which stop in the at the center position, these functions move the tables all the way from max to min and back, and never try to stop in the middle. This means there is a lot less chance of getting stuck oscillating at the center position. These are used for the dash and the load events. Basically they move they translate the tables to max and min positions, and then swap legs, and translate the opposite way.
· SwapLegs() – This function changes which legs are touching the ground (inner or outer), and strives to keep the robot at the same level that it was at before swapping legs. It also tries to keeps a constant amount of clearance between the toes.
· LazySwapLegs() – Similar to swap legs, but in conserves air by not moving the inner toes at all, and then moving the outer toes to lazy_height when they should be touching.
· TranslateOuterTo()/TranslateInnerTo() – translates the inner or outer table to a specific position in inches. Checks to make sure the position is valid, and then sets it as the refPos (reference Position).
· WaitTillThere() – Basically this function loops until all of the cylinders are at the reference Positions (all the isThere variables are true). The loop calls control every time it loops. It also exits early if someone presses Escape or Ctrl-c.
· WaitTillThereRotation() – the same as WaitTillThere, but built just for rotation so that rotation can be totally separate from everything else.
· WaitTillCommand() – this function keeps everything where it should be, and waits until it receives input from the keyboard that corresponds to a command. Then it executes that command. One command might be Center. Another might be run the dash. I have written in all kinds of commands for moving and running different events.
· MoveOuterToesTo()/MoveInnerToesTo() – Moves all the outer toes to a specific position. It does so at small intervals as a built in safety. All the toes move a certain amount, and then they wait till all the other toes are to that position as well. Then they continue like that until they reach their final position.
· MoveToeTo() – moves a specific toe to a specific position after checking to make sure the position is valid.
· PrintTable() – prints a table to the screen at a certain frequency that displays the status of each of the valves and any messages.
· dash()/loadRetrieval()/slalom()/tripWire()/objectSeeking() – event functions. These functions runs each of the events. Slalom and objectSeeking were not used in competition.
· Turn() – used to turn the robot a certain amount of degrees. Never tested!
· RotateTo() – Rotates to a certain angle in smaller degree steps. It takes a long time, but seems to work.
· PartStepBackward(int amount)/ LazyPartStepBackward() /PartStepForward()/ LazyPartStepForward()- takes part of a step forward. This is if we want to be more accurate than 22 inches. We take a part of a step. Basically we move each table a fraction of the distance we want to go, then swap the legs, and move the tables back to center.
· CrossOverPartStepForward, etc – similar to above, but for cross formation.
· WalkForward(double distance)/ WalkBackward()/ LazyWalkForward()/ LazyWalkBackward() – Use Full Steps and a partial step to move an exact distance forward or backward.
· Cross and Double functions – they do the same as WalkForward but to suit their own purposes.
· PrintMessage() – Attempt to display messages with the status screen. Never had time to get it fully working. Had some trouble with strings.
There are several things I would change, and work out in future versions of the code. First of all, as you can see above, I have many functions whose jobs are quite similar. I would probably combine a lot of these and add parameters for things such as forward, backward, lazy, cross, etc. This would create a few complex functions instead of a lot of simple functions, and would make changing things a lot easier.
We never quite got the Message output system working the way I’d have liked it to. I think it was just a matter of understanding character arrays in C and then manipulating them correctly. Most of the code is in place, it just needs a little help.
A better control algorithm could make a lot of difference. One of the upgrades we tried to implement was allowing for double deltas (the amount the cylinder can be off of it’s desired position). One bracket would be rather small, and the other a bit bigger, and surrounding the smaller one. The cylinder would have to get within the smaller bracket before it could say it was where it wanted to be. However, once it got there, the value of isThere wouldn’t revert to false until the cylinder ventured outside of the bigger bracket area. This would allow time for the cylinder to come to rest after it found where it was supposed to be, and hopefully stop some of the oscillation problems we’ve been experiencing. We tried to implement this, but it didn’t work as well as we had hoped, so we got rid of it. With proper testing, and implementation I think it would do wonders for the control.
Another solution to the oscillation problem would be implementing some sort of pulse width modulation. We tried several times to implement this, but it turned out that we couldn’t get accurate enough timing from the PC. You could either write your own timing mechanism, or use some sort of Real Time Environment.
During competition I discovered that a lot of out troubles were related to networking issues. It turned out that we were working so hard to get the code constantly running and setting shift registers, and then the networking was just pausing when it tried to output and the output buffer was filled. When the program tried to output, especially across the wireless network there would be pauses in the running of the program that would create unexpected results. This may have even been the cause of the oscillations. I would suggest writing a custom networking interface that would allow the code to continuously run regardless of the network situation, and would continue to run the last command given even if the network got disconnected. Another solution is to forget the network all together and just make a tethered controller.
Lastly, I would suggest to the next years team that they take whatever National Instruments will give them and use it. Even if it means throwing most or all of my code away, I think that would be the best decision. Their products will come with support, and will eliminate a lot of the problems we have had with input and output. If you can get their Real Time system I think you would be on the right track to having a great robot at next year’s competition. Our biggest problem at the competition this year was being able to provide precision, and speed. We opted on the side of precision, however with some help you can achieve both. If your goal is to win the next competition, which I think is very possible, then you need to get a vision or sonar system up and running. I would suggest making all the changes to the robot you want to make in the first semester of next year. By Christmas you should have a robot doing most of what it did at the competition this year. Then spend the entire second semester making small improvements, and allowing a computer science senior design team to create a vision system for the robot. They will need at least that long to get it working well.
These are just my thoughts about the future of this project. I think we’ve created a great base platform that you can build upon and make better so that we can achieve great things in the future.
I have learned so much during the course of this semester that it is hard to sum it all up here. One of the best things about the project has been being able to interact with those from other disciplines. There were three disciplines involved in the project: Computer Science, Electrical Engineering, and Mechanical Engineering. It was a challenge at times to communicate with people from other disciplines, but once we were used to each other’s styles, and jargon, we were able to merge the gaps, overlap our knowledge, and do wonders.
I’ve learned a lot about Murphy’s Law during the last semester. It seemed that anything that could go wrong did, and at the worst possible time. There were so many times that we were on the verge of walking, and then we had problems. Even at the competition Murphy didn’t leave us alone. Batteries failed, code bugs caught us off guard, and wireless network problems almost made us lose an event. Luckily we didn’t have more problems than we did.
One of the greatest aspects of this project was being able to see my code affect a physical object and make it move. It made me appreciate a lot more all of what I have learned in my CS education. I also learned how important Computer Science is to the other engineering disciplines. The other students were so happy to have me help, because they didn’t have the background or the knowledge to write the code they needed to accomplish the task.
I learned that stupid mistakes can waste a lot of time. There were so many times during the coding and testing phases of the project that I made stupid little mistakes in the code, and then spent hours or days looking for the problem. One time I accidentally wrote a statement like this:
if(…);
{
…
}
I spent so long trying to find the bug, and then I noticed the semicolon. I was so upset! I don’t think I’ll ever make that mistake again. Another mistake I made in the code was inadvertently maxing out an integer variable. I kept getting negative numbers after adding several big numbers. I finally figured out that an int was too small. Once I used a long variable the problem disappeared.
At the beginning of the project, I didn’t understand why a real time system was important. It seemed like something “nice to have”, but not necessary. Finally near the end of the project I saw how lousy computers are at doing nothing for a specific short period of time. While we were trying to implement the pulse width modulation we wanted the computer to just stop for a few milliseconds and then return to the program. The OS made no guarantees about how long it would pause for. It was awful. We could never be sure how long we were really sleeping. A Real Time OS can provide guarantees on timing issues, and using such a system would have allowed us to implement pulse width modulation.
Overall I greatly increased my knowledge of general mechanics, electronics, pneumatics, and computer hardware. I was always helping out with other aspects of the robot whether it be plugging in pneumatic tubing, testing circuits, hooking up batteries, or fixing the PC. Every day was a new learning experience.
As you can see, I learned a huge amount about all kinds of things by working on this project.
I hope this paper has helped you understand what contributions I made to the team, and even more so, I hope it helps next year’s team to be able to make leaps and bounds (or even just “steps”) of progress. There is nothing that can ever teach a person more than working on a real project such as this. It has truly been the highlight of my undergraduate education.