Circuit Design and Applied Optimization (Part 1)

It's a cold fall afternoon, and you just rushed into your afternoon circuits lab a couple of minutes. The TA greets you with your lab checkoff sheet for today. It looks like you will be learning about op-amps and the 555 timer chip. The first main checkoff has you building a circuit to make the chip produce a square wave on the output. Skipping around the material in your lab notebook, you find a simple example circuit of the chip that you will need to make.

555 Astable Circuit Diagram

555 Astable Circuit Diagram

Your notes state that this circuit produces a square wave output voltage on pin 3. You can also control the frequency and the period of the square wave by picking specific component values for R1, R2, and C. The lab manual provides you with the following equations for this relationship.

f=1,ln(2)(R1+2R2)C HzD=Duty Cycle=R1+R2R1+2R2 (×100 for %)

Okay cool; all you need to do is build the circuit in the schematic on your breadboard, power it up, and take a screenshot of the oscilloscope measuring the output square wave. It looks like you know everything you need to grab from the parts bin except three of the components: R1, R2, and C. After trying to figure out what the component values should be, you find that your checkoff sheet states you should build a circuit to make a 1000 Hz square wave with a duty cycle of 0.5=50%. It also states that you must use resistor values greater than 100Ω (for impedence and stability purposes). For practicality, you also limit the resistor values to less than 1MΩ since you know you don't have resistors any larger than that in the lab. Maybe you can work backward from here and try to solve for R1, R2, and C. You set up some equations below.


Hmm, you have three unknowns and two equations. You decide to pick a simple value for C that you know you have the part for.

C=10 nF1000=1ln(2)(R1+2R2)10×1090.5=R1+R2R1+2R2100{R1,R2}1×106

You plug this into Wolfram Alpha and let it spit out an answer.

No Solution Exsists

It looks like there is no exact solution. You call over the TA and ask how you can solve this dilemma. The TA smugly states that you just have to try some components until you close enough, and eventually, you will get better at guestimating component values throughout your lab this semester. You think this is utter nonsense and start hatching a plan to come up with a better solution.

After staring at your equations for a little you realize you can formulate this as an optimzation problem! If you can have your computer start guessing values for R1 and R2 in a smart way to get f and D close to what you want, your computer can figure out how good the guess was and adjust R1 and R2 to give even better results. You quickly jot down the optimization problem.


You have created two objective functions, fobj and Dobj. For a particular pair of values R1, R2, these objective functions tell you how off the circuit is from having the right frequency and period. These are distance functions whose value is always greater than 0; the further from zero, they are the more far off you are from your target. The previous equations you wrote show that these objective functions are the same, just with the 1000 and 0.5 on the right side and absolute value to get the distance between the value you want and the value calculated for a pair of resistor values.

For a perfect circuit, aka a perfect selection of R1 and R2 to match the right values for f and D, you want fobj and Dobj to be as close to zero as possible. Normally, you would only have a single objective function to optimize towards one target in an optimization problem. Still, here you have two objectives, one for frequency and one for the duty cycle, as taken from your lab instructions. For now, you add them together and optimize the resulting sum, effectively giving the two goals the same weight/value. You note this objective importance weighting idea for later just in case it's handy. Another tweak you have made is to also scale each objective function by the calculated value for f and D so that both objectives are of the same magnitude. Without this, a calculated absolute error for f may be in the hundreds, while a calculated absolute error for D is in the zero to one fractional range. Adding them together would result in the absolute error for f overpowering the absolute error for D. The scaling factor always makes sure they are in the same comparable range so that adding them together is a roughly equally weighted sum of both objective goals.

Realizing that there are only two variables, you quickly put together a plot to show the value of your objective functions for many different combinations of R1, R2. The resistor values are on a log scale since the resitor values in the lab follow a log scale as well. More on this detail later.

Initial Objective Function Plots
Initial Objective Function Plots

At first glance, it appears that the duty cycle objective function is always between 0 and 1, while the frequency objective function is from 0 to around 17. Adding them results in an unequally weighted sum. You can add some weights to your objective function sum to make sure they are roughly equal in magnitude.


You make another plot with this added ajsutment, you also make the color value corresponding to the objective function follow a log plot to accentuate the smaller values.

Objective Function Plots with Objective Weights and Log Scaling
Objective Function Plots with Objective Weights and Log Scaling

Perfect! Now it's super clear where the minimum of the objective function is. All you have to do now is write a couple of lines of code in python using Scipy's optimization library. After running your code, you get the following solution. You realize the default solvers as part of the minimize function, keep settling on values nowhere near the minimum on your plot. It seems that the resulting final objective value reached is very sensitive to the initial guesses for R1 and R2 you have to enter when defining the optimization in your code. This could be because of the nature of the multi-objective function, plateaus and valleys in your objective function, and gradient calculations used.

After some poking around the Scipy documentation, you realize what you need is a global optimization function. Scipy's minimize function is suitable for solving local minimums, which is highly sensitive to your initial guess and can get you stuck in valleys in your objective. A global optimization function will be able to search your whole objective function to avoid getting stuck in a single spot and not worry about your initial guess affecting the final answer.

You find the differential_evolution global optimization function that Scipy has under its global optimization functions section. It appears that this optimization technique works using candidate guesses and mutating and evolutionarily selecting them to generate better and better guesses. Sounds perfect to you, so you run your updated code using differential_evolution and get a good answer this time.

R1=100.0525,R2=72089.7984f=999.9977,D=0.5003f % error=0.0002,D % error=0.06

Objective Function Plots with Solution
Objective Function Plots with Solution

Now that you have some good values for R1 and R2, you quickly run over to the resistor parts bins. You realize that the actual resistor values available come in set values distributed on a log scale. These scales are called E series. E series are based on a base 10 system where the E number determines how many component values are in each log scale step. For example, E12 series resistors have vales 12 log distributed values between any given power of 10. An example set of values from the E3 series is as follows: 1.0,2.2,4.7,10,22,47,100,220,470,1k,2.2k,4.7k,10k. One final detail is that you have a minimum and maximum value for your set of components. In the E3 example, the min value was 1×100 and 1×104. The purpose of this is to make sure that any component is within a certain percent tolerance from the nearest values. With the E3 series example, each value is always 40% away from the previous or next value in the series. A more common series is the E24 series which has a 5% tolerance between values. You realize right now that all this information is irrelevant as you can just pick the nearest valued components to what you calculated for R1 and R2. The resistors in the parts bins are E24 resistors, so you quickly grab a 100 Ohm resistor and a 7.5×104 Ohm resistor. You plug these into your original equations and get the following.

R1=100,R2=7.5×104f=961.2213,D=0.5003f % error=3.8779,D % error=0.06

It looks like picking the nearest component value has led to slightly more error, but it's close enough. You finally build your 555 timer circuit and get the circuit checked off by the TA. As you skim the next part of your lab, you start to wonder if there is some way to integrate component selection into your optimization approach you have developed.