First Year EECS Projects @UC Berkeley

Simon Zirui Guo
16 min readAug 27, 2020

--

Debug, debug, debug… Somehow it all worked!

In my freshman year at Berkeley, I did a lot of projects for Electrical Engineering and Computer Science courses. I think those projects taught me way more than doing problem sets or preparing for exams, and they became a place to preserve my hacker spirit under intense academic pressures.

I like to think about doing projects as a combination of rigorous thinking, purposefully trying, and having a flow of creativity.

Maybe you think about it as to be formal and be scrappy. I think the picture below really captures the idea (you might find a lot more examples later too). This is a very simple setup on building a 3-bit Digital-to-Analog Converter (DAC).

Be formal: have a good set up and procedures about doing things, having your plan at the start, record everything, shut off power source before config, etc.

Be scrappy: See the laptop that is oriented sideways? Well, that’s sketch, but I get to see the schematics nicely. See the microcontroller in mid-air? That’s probably not good for wires but it made sure I don’t short my board with little metal pieces on the desk.

To me, being an engineer means working towards mastery of both worlds; be systematic but also be flexible.

An 8-bit Digital-to-Analog Converter (DAC) in action.

In this blog, I will showcase some of the projects I worked on for my coursework this year. I will explain what the project is, as straightforward as possible (basic CS & EE knowledge required), how it works(or at least how I think it does), what are some of the key concepts I learned by doing this, as well as what I struggled with it (which I think is the most important part).

Disclaimer: None of the code or circuit designs in those pictures shown below is publically accessible, and none of the images/videos show exact solutions or significant hints, in compliance with the Berkeley EECS department’s guideline on academic honesty and plagiarism.

Tip: There are a lot of projects below, feel free to glance through, and read more on things that seem interesting to you.

CS 61A: Structure and Interpretation of Computer Programs

A course focused on functional programs, introductory computer science ideas such as recursion and higher-order functions, building abstraction layers. We learned Python, SQL, Scheme (Lisp).

I struggled a lot with this class, mostly actually due to my previous CS knowledge, which is built very differently than this. Learning to unlearn and relearn is painful, but I am glad it showed me a new perspective and fix many of my misconceptions.

Hog

A 2-player dice game with tons of tricky rules that modify score based on the number of dice you roll and the resulting dice numbers, but whoever gets 100 first wins.

How it works: Tons of control statements (implementing complex rules) and higher-order functions to generate and update game logic.

Concepts: Control Statements, Higher-order Functions

Struggle: My first try with lots of higher-order functions, definitely not as straight-forward as object-oriented programming. I had to wrap my head around thinking from a functional logic standpoint rather than object-based state management.

Cats (Autocorrected Typing Software)

Autocorrect and typing test in action. The bold green texts show that is autocorrected, and red shows you need to manually configure it.

Typing fast is hard; thus, we put autocorrect. This is an interface that helps you increase typing speed while fixing words that you accidentally typed wrong.

How it works: Program chooses sample sentences (usually wise quotes), and monitor your typing skills as you go. When there is an error, it will attempt to see if this is a minor error worth autocorrecting.

Concepts: Abstraction, Recursion

Struggle: The hardest thing was seeing which word is worth autocorrecting, as some are serious mistakes that people should fix manually to learn. I wrote a recursive function to compute the minimum number of operations to turn wrongly typed words into desired words, and to guide the decision on whether to autocorrect or not.

Ants (Similar to Plants vs. Zombies)

Here we have Ants vs. Bees instead of Plants vs. Zombies. Like the classic game, you have food to arrange more ants to stop and attack the bees. All ants have different abilities.

How it works: We did this project when we were first introduced to objects and inheritance. There are some generic insect classes, and we build further subclasses by having bees and ants, ants with different capabilities, etc. Many functions are updating local states between insects after attacks and interacting with the general map/environment.

Concepts: Functional Programming, Object-Oriented Programming

Struggle: Combining a lot of functional programming and object-oriented programming in Python can confuse tracking what is going on. Since there are so many insects all attacking each other, I had to get used to setting up scenarios and checking all objects’ state, which helps me configured this large program.

Scheme (Python Compiler for Scheme)

Some convoluted Scheme statements to show this compiler does work!

Lisp is weird to understand and execute, and we wrote a Python-based compiler to process it. This compiler covers most key features of the scheme language and can process all kinds of scheme codes.

How it works: The compiler takes scheme expressions, tokenize the input and recursively parses then evaluates the expression to convert into Python equivalent representation.

Concepts: Tree-recursive, Lexical Analysis, Syntactic analysis

Struggle: Scheme is uniquely in the sense that it can delay the processing of expression using quotes, so I have to handle that during parsing and choosing what to evaluate. This is a tree-recursive program that can itself evaluate other recursive programs (sounds very recursive, isn’t it). Still, the whole program is quite short, which shows me how elegant but powerful this functional programming language is.

EECS 16A: Designing Information Devices and Systems I

Introduction to circuitry analysis (voltage dividers, op-amps), system thinking, linear algebra, and basic ML concepts such as OLS and OMP. Microcontroller code is written in ArudinoC-like code, and all other software is in Python.

I found this course showed me how cool common devices around us are designed, such as cameras, touchscreens, and GPS, and how powerful linear algebra while used with hardware to collect and process information.

Imaging (Single Pixel Camera)

The single-pixel camera captures information by masking each pixel and assembling the image after some linear algebra

Here is a camera, and there is no CMOS or CCD. This is just a single light sensor (photoresistor), but it can capture pretty good pictures with a unique procedure of masking (although very long).

How it works: A projector projects a mask onto the picture, leaving one square blank for the targetted pixel we want to scan with our photoresistor. We repeat this with a different mask for all pixels, and we can assemble the whole image.

Concepts: Circuits, Matrix and Vectors, Linear Systems

Struggle: I was in disbelief that this actually worked, with such cheap hardware but a procedure to get more info to the system step by step. We also tried some other kinds of masks to see if we can use less information to infer the picture, thus making the process faster.

Touch (Resistor and Capacitor-based Touchscreen)

Resistor-based touch screen vs Capacitor-based touch Screen

We built two touch screens to detect a touch in various positions, using very different approaches and principles. See them in action here (resistor-based) and here (capacitor-based).

How it works: For the resistor touch screen, we use a thin film on top of a matrix of resistors that form voltage dividers. When you apply pressure to the film, it touches the voltage dividers, and thus, we can detect where on the film you touched based on voltage readings. For the capacitor touch screen, since your figure has water, when you place it on two thin plates, it changes the capacitance, which we can observe by checking the voltage.

Concepts: Voltage Divider, Capacitance

Struggle: For the resistor touchscreen, we made a mini matrix of resistors, so we had to detect signals in 2 directions (vertical and horizontal) to locate the touch. We added a comparator (Op-Amp) logic for the capacitor touchscreen to show that the screen is touched by lighting an LED.

Acoustic Positioning System (Simulate GPS Localization)

Left: We place a microphone surrounded by 6 speakers sending sound patterns and use some linear algebra magic to detect where the mic is.

GPS localization is magical, and we try to simulate it with sound since it is harder to play with satellites. We have one microphone as a GPS client and six speakers as satellites around the earth, sending unique sound patterns. With some linear algebra tricks, we can locate the microphone relative to the speakers.

How it works: The speakers send sound structured as “Gold Codes,” uniquely identifiable codes that are orthogonal to each other. The microphone picks sound up (all the speakers all at once) and identifies each source by Gold Code signature using orthogonal matching pursuit. By the length of code received, we can then tell the distance to a particular source, hence helping us identify the microphone’s location.

Concepts: Orthogonal Matching Pursuit, Linear Algebra

Struggle: Lab space is noisy, and background noise creates some interference with speakers’ sound signature. However, using orthogonal matching pursuit is quite robust, and we can still identify signatures in the presence of noise.

CS 61B: Data Structures

A course that talks a lot about object-oriented programming, data structures (Linked List, Queue, Trees, HashMaps, etc.), runtime performance, graphs, search and sorting algorithms. All of it was taught in Java.

This course was a lot of work, but it seemed much more straightforward to me. The data structures I learned was like a box of legos, and by piecing them together, it was quite fun to see how I can design a solution that achieves design goals.

Signpost (Board Game)

Left: Me trying to solve the game via GUI (I am not really good). Right: The program shows the solution.

A board game that shows different arrows that you need to connect them in order and go from start to finish using all arrows.

How it works: I had different classes for a square, a place on the board, a board, groups, with instance and class functions that enable game functions.

Concepts: Object-Oriented Programming, State management

Struggle: As with all board games, there are a lot of rules and edge cases. For example, a simple connect and disconnect action might have many side effects. I wrote a lot of unit tests to test them and make sure they are robust enough, which saved me a lot of hassle in integration testings.

Enigma (Yes, the WWII German Machine)

Left; A German Enigma machine, with rotors, lights, plugboards shown (Source: Wikipedia) Right: A set of rotor codes German Navy uses, which I used in my general purpose Enigma machine
Encrypt and Decrypt texts using my enigma program

Remember the enigma machines from WWII movies? Well, this project creates an enigma machine replica, but much more general and powerful with an unlimited combo of codes and settings. You can encrypt and decrypt texts using it, as long as you know the machine’s setting.

How it works: The Enigma machine contains rotors, plugboards, and lights that show the decrypted alphabet. The original machine uses complex electrical and mechanical structures, and here I replace it with carefully designed Java classes that each represent those physical objects.

Concepts: Object-oriented Programing, Choosing the right combinations of Data Structures, Regex

Struggle: Understanding the mechanical structure of the enigma is hard without physically playing with it. I drew down at each step how an alphabet signal is transformed inside the machine, and come up with Java class designs that stimulate the process.

LOA (Game-Tree AI)

Right: I got too lazy to play so I set my player to auto and beat the other AI.

Another board game! This time you are trying to move your pieces (with many limitations on where), eat your components’, and whoever gets all of their pieces all connected or contiguous wins. This game is already tricky to play, and you have to write an AI to do that for you.

How it works: When in auto mode, the program will create a search tree, evaluating possible steps by the components. By evaluating chosen heuristics, it will select the action that maximizes the chance of winning, after considering several layers of next steps.

Concepts: Game Tree, Search Algorithm

Struggle: This might be the first game AI I made, and it was quite hard to choose a heuristic for the game tree. I found sometimes the best heuristics are not necessarily the most complex; it is hard to think about this process intuitively.

Gitlet (Git version control systems)

Left: A simple demo showing its version control capability. Right: Gitlet architecture (Source CS 61B TA)

I use Git almost every day, but this time I have to rebuild it in Java. From scratch, I built a Git that can do most of its functionalities, such as add, commit, status, log, checkout, branches, merge (& merge conflicts labeling).

How it works: Git uses a Merkle DAG data structure (which I become quite familiar by teaching blockchain), and this program uses Java serialization for file IO & persistence.

Concepts: Hashing, Content-based Addressing, Persistance Design

Struggle: Coding this is relatively straightforward, but coming up with both elegant and robust design took me the most time. I spent a lot of time writing design doc and go back and forth, changing my design as I progress. I drew some inspiration on how Git was implemented, and I really admire its genius design after implementing it myself.

EECS 16B: Designing Information Devices and Systems II

A course on transient circuit responses, phasor analysis, controlling linear systems (linearization, system ID, feedback control), and introductory ML techniques such as SVD and PCA. Microcontroller code is written in ArudinoC-like code, and all other software is in Python.

Most people at Berkeley don’t quite like this course, but I surprisingly do! I think it is very magical how linear algebra and control theories can control complex electrical circuits and systems.

Color Organ

Demo, Color Organ in Action!

A combination of low-pass, high-pass, and band-pass filters activate different LEDs based on the microphone signal; See how the light dances to the beat!

How it works: Circuit breaks the microphone signal into different frequency ranges via filters, and corresponding LEDs lights up.

Concepts: Filter Design, Cut-off Frequency

Struggle: Finding the components that create dedicated cut-off frequencies, lots of debugging using a signal generator and oscilloscope.

SIXT33N: Voice-Controlled Car

Left: Our car circuitry with filters, motor controllers, encoders, voltage regulators. Right: Clustering voice commands in PCA space.

A car that turns in various directions based on voice commands. You need to configure car parameters, drive the car straight, and classify voice command.

How it works: Voice input gets processed using PCA and clustering to identify command, the car uses closed-loop feedback-control to drive straight and turn, with encoders on motors to detect the speed of each.

Concepts: System Identification, Filters, Feedback Control, Principal Component Analysis, Clustering

Struggle: Getting a car to run straight is way more challenging than you think, with feedback control on cheap motors. See the video below (we tried our best on the slippery wooden floor). Our motor fried in the end, and we couldn’t get a replacement due to COVID.

Linear–Quadratic Regulator (LQR): A Robust Motor Controller

LTSpice simulation of our closed-loop LQR controller bringing motor under control even there was a model mismatch. Circuit blurred to prevent plagiarism.

Simulate a DC motor system using an RL circuit, and use closed-loop LQR controller to bring the motor under control even in a situation where there is a model mismatch (the parameter is system is very different than actual).

How it works: LQR controller uses a feedback control loop that feeds new discrete-time input based on a weighted current state. The weight on the current state is computed by minimizing the cost function we designed.

Concepts: Linear Model, Controllability, Feedback Control, Spice

Struggle: Finding the optimal matrices for cost function and input weights to quickly bring the system under control while satisfying input constraints.

Data 100: Principles and Techniques of Data Science

A course on how to think and use data science, covering concepts such as exploratory data analysis, data cleaning, classic supervised learning (linear regression, logistics regression, decision trees, random forest), and classic unsupervised learning (principal component analysis, clustering). All assignments are done in Python and SQL, with emphasis on Pandas library.

I like this course as it introduced me to inferential thinking and practical skills to generate insights from data. I also build a pretty solid intuition of classic ML techniques, which made me much more confident to learn ML.

Food Safety (SF Restaurant Data Analysis)

Distribution of inspection scores and restaurant risk levels over the years

Playing with restaurant cleanness inspection score in San Francisco and trying to generate some insights and put in visual presentation.

How it works: Combining various public databases on restaurant information, inspection history, and inspection reasonings, to conclude why restaurant fail inspections and if they improve over the years.

Concepts: Pandas, Exploratory Data Analysis, Primary vs. Foreign Key, Visualization

Struggle: Public databases are messy, and I had to do a lot of formatting and cleaning! However, missing values do not necessarily mean I can simply throw them away. I had to think about why that data wasn’t logged; the missing values become valuable features themselves.

By the end of this project, I know which restaurant in SF I should not try.

Spam/Ham Classification

Left: My design matrix with 518 features (that is a lot) and the resulting training and validation accuracies. Right: ROC curve to see the model performance.

Logistical regression-based classifier to decide if an email is a spam or not. I got a 95% accuracy on the test set, which was much better than I thought and a pretty good ROC for precision and recall.

How it works: Email data is transformed, and key features were extracted, trained using a logistical regression model with some regularization parameter, and model is used to predict new unseen data.

Concepts: Feature Extraction and Engineering, Logistical Regression, Regularization, Cross-Validation

Struggle: The most challenging thing was coming up with features that can separate spam and ham emails. I won’t go to the specifics, but it was a lot of comparing data, manually observing the data, and lots of trials and errors using the validation set, to bump up accuracy, recall, and precision.

After this project, I have massive respect for Gmail’s spam detector and won’t get as frustrated when it classified things wrong. Also, I know what features I should not include in my email to avoid misclassification.

An exchange between professors and students in Soda Hall basement, “Git some sleep!”, the picture was taken on Valentine’s Day

Key Takeaways from all those Projects:

  1. Read the Docs! Or in Prof. Hilfinger’s words, “RTFM.” I found most of my bugs or confusion were resulting from incorrect understandings and neglecting details. However, don’t always trust the doc. I have spotted a lot of mistakes in those and talked to the TAs. Verify with your logic to see if it actually makes sense.
  2. Draw it out! This is perhaps the most useful thing I have found. Draw the architectures or circuits down instead of just remembering in mind, and I can follow the logic flow quite logically and intuitively.
  3. Test! Write Tests! Run Tests! This is something Berkeley has taught me systematically. I used to do things pretty ad-hoc, throwing in a printf somewhere, but now I know how I can use separation of concerns to make my life easier. I write excessive unit tests, covering all kinds of edge cases I can think of, and that saves me so much hassle in integration tests. I often go back and forth to add unit tests, and re-run tests making sure things don’t break again (I guess you can call this a simple CI/CD)
  4. Don’t Get Ready, Get Started! This was a phrase I learned at the IDEO hackathon last year, but I finally understood it by doing these projects. These projects are long and large-scale. It is impossible to think through everything before starting or have a perfect game plan; you just got to jump in and start building. I might delete or refactor my code over and over again, but that is part of the process to get me near the final product.
  5. Write Down your Thoughts. I like to write comments as I write down code, even though mostly the texts will get deleted or reorganized. By doing this, I lay down my logic and offload my brain, so I won’t have to remember as I am thinking hard to build out the code. CS 61B also showed me how important it is to have a nicely compiled Javadoc or a design document to write down what methods I wrote and what they do (increase code reuse by so much!)
  6. Beautify your code as much as possible. Although I might be scrappy, once something seems to work decently, I will refactor it to be as reusable as possible. I like things that are pure, modular, and simple, and combining those things is just pure joy.
Nothing feels better when you passed all the integration tests at your first try and you can go to sleep.

Concluding Thoughts

With my first semester course CS 61A professor John Denero; he gave us a sticker showing lambda after finals, showing how much he likes functional programming

I am incredibly thankful for my project partners in these courses (Lukas Chang, Todd Yu, Grace Lam, Richard Liu, Selina Kim) and many friends, upperclassmen, and TAs that helped me out.

These projects are behind the genius minds of professors and TAs, and I am grateful they designed in a way that allows us to learn techniques systematically while challenging us to use our creative energy.

In fact, professors really care about how students think about these projects. For example, Prof. Josh Hug reached out to me on Zoom after seeing my Zoom chat comment.

As someone who used to do hackathon projects (30 by the time I wrote this) and hack things up quickly, studying at Berkeley EECS showed me some important engineering principles, such as setting up abstraction layers, practicing separation of concerns, frequently conducting unit and integration tests, and think in terms of systems.

That being said, I miss the hackathon days; I wish I could have exercised more of my creative energies while doing projects and can do more things outside of school projects. The training I got equipped me with the right framework for thinking about engineering projects, and I believe that will support my creativity in the future. To be systematic in the way I think about problems, but also be scrappy and resourceful in the process of building.

So what’s next? Well, my sophomore year starts today again. This semester, I am focusing on learning more low-level computer architecture and get a better understanding of the physics behind electrical engineering. With COVID, I miss the days that I can play around with hardware, so whenever I get back to campus, I plan to do several hardware courses, such as FPGA & ASICs, robotics, mechatronics, and fabrications.

--

--

Simon Zirui Guo

Accelerating Deep Tech | Robotics, Blockchain, Neurotech | EECS @UCBerkeley | Teaching @CalBlockchain, Director @BB_Xcelerator | prev @hax_co, @SOSV, @Interaxon