Sensor Daisy Chain#
This tutorial will analyze a string of sensors wired as a daisy chain where the string is powered from one end. This configuration is common in e.g. instrumentation of production lines, seismic acquisitions systems etc.
The system consists of the following components:
A main acquisition unit, powered from a 14.4V Li-ion battery. A voltage booster powers the daisy chain with 48V.
16 sensor units, spaced 16m apart and connected with twisted pair wire for power. The sensor units consume 0.35W from 3.3V. The power input is polarity protected with a diode. A power switch enables power to the next unit.
from sysloss.components import *
from sysloss.system import System
import pandas as pd
import matplotlib.pyplot as plt
System definition#
The system is defined using the System class.
Tip
When you want to analyze a system with variations on specific parameters, it can be very effective to define the system in a function, with target parameters as function arguments.
Since we have 16 identical units, it is practical to use a loop to add the components. Wire resistance depends on wire gauge, and we define that as a parameter to the create_sys() function below.
Note
Component names must be unique in the system. Use e.g. an index added to the name.
def create_sys(wire_res=4.3, vdrop=0.54, eff=0.72):
sdc = System("Sensor Daisy chain", Source("Li-ion", vo=14.4, rs=0.2))
sdc.add_comp("Li-ion", comp=Converter("Boost 48V", vo=48.0, eff=.88))
parent = "Boost 48V"
for i in range(1,17,1):
idx = " [{}]".format(i)
sdc.add_comp(parent, comp=RLoss("Twisted pair"+idx, rs=wire_res))
sdc.add_comp("Twisted pair"+idx, comp=VLoss("Diode"+idx, vdrop=vdrop))
sdc.add_comp("Diode"+idx, comp=PSwitch("Power switch"+idx, rs=0.03, ig=10e-6))
sdc.add_comp("Diode"+idx, comp=Converter("Buck 3.3V"+idx, vo=3.3, eff=eff))
sdc.add_comp("Buck 3.3V"+idx, comp=PLoad("Sensor unit"+idx, pwr=0.35))
parent = "Power switch"+idx
return sdc
This is a deep power tree (~50 levels), so let’s look at the last few nodes only:
sdc = create_sys()
sdc.tree("Twisted pair [12]")
Sensor Daisy chain
└── Twisted pair [12]
└── Diode [12]
├── Buck 3.3V [12]
│ └── Sensor unit [12]
└── Power switch [12]
└── Twisted pair [13]
└── Diode [13]
├── Buck 3.3V [13]
│ └── Sensor unit [13]
└── Power switch [13]
└── Twisted pair [14]
└── Diode [14]
├── Buck 3.3V [14]
│ └── Sensor unit [14]
└── Power switch [14]
└── Twisted pair [15]
└── Diode [15]
├── Buck 3.3V [15]
│ └── Sensor unit [15]
└── Power switch [15]
└── Twisted pair [16]
└── Diode [16]
├── Buck 3.3V [16]
│ └── Sensor unit [16]
└── Power switch [16]
Analysis#
In the analysis phase we will check the power efficiency of the daisy chain with different wire gauges. Note that current runs both directions on the power pair, so effective resistance is twice that of a single wire.
Wire gauge |
Resistance (ohm/m) |
|---|---|
20 |
0.0333 |
22 |
0.053 |
24 |
0.084 |
26 |
0.134 |
28 |
0.213 |
Tip
Use the tags argument of the .solve() function to tag the results table with custom columns.
wlen = 16 * 2 # total wire length between sensor nodes
wirelist = {"AWG20": wlen*0.0333, "AWG22": wlen*0.053, "AWG24": wlen*0.084, "AWG26": wlen*0.134, "AWG28": wlen*0.213}
res = []
for wire in wirelist.keys():
sdc = create_sys(wire_res=wirelist[wire])
res += [sdc.solve(tags={"Wire":wire})]
df = pd.concat(res, ignore_index=True)
df
| Component | Type | Parent | Wire | Vin (V) | Vout (V) | Iin (A) | Iout (A) | Power (W) | Loss (W) | Efficiency (%) | Warnings | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Li-ion | SOURCE | AWG20 | 14.4 | 14.258142 | 0.709291 | 0.709291 | 10.213786 | 0.100619 | 99.014874 | ||
| 1 | Boost 48V | CONVERTER | Li-ion | AWG20 | 14.258142 | 48.0 | 0.709291 | 0.185408 | 10.113168 | 1.21358 | 88.0 | |
| 2 | Twisted pair [1] | SLOSS | Boost 48V | AWG20 | 48.0 | 47.802429 | 0.185408 | 0.185408 | 8.899588 | 0.036631 | 99.588394 | |
| 3 | Diode [1] | SLOSS | Twisted pair [1] | AWG20 | 47.802429 | 47.262429 | 0.185408 | 0.185408 | 8.862961 | 0.10012 | 98.87035 | |
| 4 | Buck 3.3V [1] | CONVERTER | Diode [1] | AWG20 | 47.262429 | 3.3 | 0.010285 | 0.106061 | 0.486111 | 0.136111 | 72.0 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 410 | Diode [16] | SLOSS | Twisted pair [16] | AWG28 | 23.680869 | 23.140871 | 0.021017 | 0.021017 | 0.497691 | 0.011349 | 97.719678 | |
| 411 | Buck 3.3V [16] | CONVERTER | Diode [16] | AWG28 | 23.140871 | 3.3 | 0.021007 | 0.106061 | 0.486111 | 0.136111 | 72.0 | |
| 412 | Sensor unit [16] | LOAD | Buck 3.3V [16] | AWG28 | 3.3 | 0.0 | 0.106061 | 0.0 | 0.35 | 0.0 | 100.0 | |
| 413 | Power switch [16] | PSWITCH | Diode [16] | AWG28 | 23.140871 | 23.140871 | 0.00001 | 0.0 | 0.000231 | 0.000231 | 0.0 | |
| 414 | System total | AWG28 | 0.960525 | 13.831559 | 8.231672 | 40.486306 |
415 rows × 12 columns
The results table is now quite large - let’s look at the System total only:
df[df.Component == "System total"][["Component", "Wire", "Power (W)", "Loss (W)", "Efficiency (%)"]].style.hide(axis='index')
| Component | Wire | Power (W) | Loss (W) | Efficiency (%) |
|---|---|---|---|---|
| System total | AWG20 | 10.213786 | 4.613818 | 54.827546 |
| System total | AWG22 | 10.412506 | 4.812548 | 53.781077 |
| System total | AWG24 | 10.768457 | 5.168515 | 52.003197 |
| System total | AWG26 | 11.507698 | 5.907774 | 48.662414 |
| System total | AWG28 | 13.831559 | 8.231672 | 40.486306 |
From the first table we can see that the output voltage from the last sensor node is 23.16V. Feeling optimistic - will it work with AWG30 also?
sdc = create_sys(wire_res=16*2*0.338)
sdc.solve()
.solve() now throws an exception: The input voltage to sensor node 14 drops to zero. There is no steady-state solution for this system, so AWG30 is not an option. It would work with 13 sensor nodes, however.
Finally, let’s plot the voltage along the daisy chain for the different wire gauges.
for i in range(0,9,2):
plt.plot(range(1,17,1), df[(df.Component.str.startswith("Diode")) & (df.Wire=="AWG2"+str(i))][["Vin (V)"]].values, marker='.', label="AWG2"+str(i))
plt.legend()
plt.xlabel("Sensor node #")
plt.ylabel("Input voltage (V)")
plt.grid()
plt.title("Daisy chain voltage distribution");
Improve accuracy with parameter interpolation#
Both current and voltage drop significantly along the daisy chain. The input voltage affects the 3.3V buck converter efficiency, and the current affects the diode forward voltage drop. Next, we define interpolation data for the diode voltage drop and converter efficiency, both as a factor of output current. The data points are extracted from the component’s data sheet.
diode_vdrop = {"vi": [48], "io":[1e-3, 1e-2, 0.1, 1, 2, 3, 5], "vdrop":[[.25, .325, 0.4, 0.65, 0.8, 0.9, 1.1]]}
buck_eff = {"vi": [48, 36, 24, 12], "io": [1e-3, 10e-3, 0.1, 0.2, 0.3],
"eff":[[0.61,0.63,0.66,0.68,0.69],[0.63,0.65,0.68,0.705,0.72],[0.66,.68,0.72,0.73,0.74],[0.7,0.72,0.76,0.78,0.77]]}
res = []
for wire in wirelist.keys():
sdc = create_sys(wire_res=wirelist[wire], vdrop=diode_vdrop, eff=buck_eff)
res += [sdc.solve(tags={"Wire":wire})]
df = pd.concat(res, ignore_index=True)
df[df.Component == "System total"][["Component", "Wire", "Power (W)", "Loss (W)", "Efficiency (%)"]].style.hide(axis='index')
| Component | Wire | Power (W) | Loss (W) | Efficiency (%) |
|---|---|---|---|---|
| System total | AWG20 | 10.686804 | 5.086870 | 52.400460 |
| System total | AWG22 | 10.872031 | 5.272123 | 51.507468 |
| System total | AWG24 | 11.201731 | 5.601822 | 49.991459 |
| System total | AWG26 | 11.853800 | 6.253918 | 47.241240 |
| System total | AWG28 | 13.545782 | 7.945931 | 41.340182 |
Let’s look at the converter efficiency and diode power loss as a function of wire gauge:
for i in range(0,9,2):
plt.plot(range(1,17,1), df[(df.Component.str.startswith("Buck")) & (df.Wire=="AWG2"+str(i))][["Efficiency (%)"]].values, marker='.', label="AWG2"+str(i))
plt.legend()
plt.xlabel("Sensor node #")
plt.ylabel("Efficiency (%)")
plt.grid()
plt.title("3.3V buck converter efficiency");
for i in range(0,9,2):
plt.plot(range(1,17,1), df[(df.Component.str.startswith("Diode")) & (df.Wire=="AWG2"+str(i))][["Loss (W)"]].values, marker='.', label="AWG2"+str(i))
plt.legend()
plt.xlabel("Sensor node #")
plt.ylabel("Loss (W)")
plt.grid()
plt.title("Diode power loss");
Tip
Double-check interpolation data by using the plot_interp() method. 2D data can be plotted as either a 2D color map or 3D surface, with or without input data marked. Interpolation outside of the input data points is using the last valid data point as value.
sdc.plot_interp("Buck 3.3V [1]");
sdc.plot_interp("Buck 3.3V [1]", inpdata=False, plot3d=True);
sdc.plot_interp("Diode [1]");
Summary#
This notebook demonstrates how defining a system creation function helps explore system parameters. Further on 2D parameter interpolation is used to define converter efficiency as a function of both input voltage and output current.