Three Kingdoms on One Timeline
A single plant signal — and within it who was nearby: nobody, a human, a dog, a sheep. The three kingdoms meet in one bioelectric trace.
In a nutshell
What: You take the bioelectric signal of one plant and let a model guess which creature was near it — nobody, a human, a dog or a sheep. It is the book's grand finale: plant, animal and human lie side by side on a shared timeline, read with the same procedure as everything before.
The core idea: Two convergences become visible. First that of the method — again signal → features → model, exactly as with voice, cat sound and heartbeat. Second that of the result: the plant, the silent kingdom, carries in its voltage curve traces of the other two kingdoms.
You need: the 3-second recordings from the COIN study (see below), Python with
librosa, scikit-learn and xgboost. Without the study data it works
too — with your Biolingo sensor from 11.1.
What it's about
For four parts you have learned to read single beings — humans, animals, plants, whole groups. This chapter finally lays them side by side and shows the astonishing thing: it was always the same procedure, and always the same signals. Here you make this convergence tangible yourself — in a single case where all three kingdoms come together.
In a study from the COINs seminar, students clipped a home-built sensor to a basil plant and set different guests before its nose one after another: sometimes nobody (baseline), sometimes a human, sometimes a dog (a carnivore), sometimes a sheep (a herbivore). Then they asked: can you guess from the plant signal alone who was there? The answer was a surprisingly clear yes — and exactly this you rebuild.
Before you start — the data
The original data come from the COIN study by Rokicki, Stricker, Schäfer and Shi (University of Cologne,
WS 2025/26): 39 recordings, cut into 3-second windows, four classes. Ask the seminar for the WAV files
and put them in four folders: data/baseline/, data/human/, data/dog/,
data/sheep/. Without the study data: record your own with the Biolingo sensor (11.1) —
a few minutes each of "nobody there" versus "a human sits beside it", and, if you like, a pet. Two classes
already suffice to show the principle.
A little background
What the plant gives away. The study found clear, statistically significant differences (Welch test, p < 0.05). Human and dog made the signal more restless — more variance, more noise. The sheep, by contrast, made the signal energy drop, a kind of falling silent — perhaps, the authors suspect, an old reaction to a herbivore. An XGBoost model reached about 83 per cent hits across all four classes, with F1 values above 90 per cent for human and dog; the sheep stayed hardest.
Again an image from the signal. The procedure is familiar to you: the sensor (an OPA2134 amplifier on an ESP32, 380 Hz) cuts the signal into 3-second pieces. From each piece we pull the same sound features as with the cat in 12.1 — MFCCs, spectral centroid, zero-crossing rate, energy and their rates of change. In short: we listen to the plant as if its signal were a sound.
Extract features and train the model
First the features per 3-second window, then XGBoost — the study's winning model. The full code is on GitHub.
import librosa, numpy as np, glob, os
from xgboost import XGBClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
def features(file):
y, sr = librosa.load(file) # load 3-second window
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
delta = librosa.feature.delta(mfcc) # rate of change
centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
zcr = librosa.feature.zero_crossing_rate(y)[0]
energy = librosa.feature.rms(y=y)[0]
return np.concatenate([
mfcc.mean(axis=1), mfcc.std(axis=1), # shape + volatility
delta.mean(axis=1), # how fast the signal changes
[centroid.mean(), centroid.std(), # frequency centre
zcr.mean(), zcr.std(), # roughness
energy.mean(), energy.std()], # intensity
])
CLASSES = ["baseline", "human", "dog", "sheep"]
X, y = [], []
for c in CLASSES:
for file in glob.glob(f"data/{c}/*.wav"):
X.append(features(file)); y.append(c)
X = np.array(X)
print("read:", len(X), "windows")
enc = LabelEncoder().fit(y)
Xtr, Xte, ytr, yte = train_test_split(X, enc.transform(y), test_size=0.2,
random_state=0, stratify=y)
model = XGBClassifier(objective="multi:softprob", num_class=len(enc.classes_),
eval_metric="mlogloss")
model.fit(Xtr, ytr)
print("overall hits:", round(model.score(Xte, yte), 2))
print(classification_report(yte, model.predict(Xte), target_names=enc.classes_))
What you should see
Human and dog the model recognises almost surely (high F1 values) — their restlessness in the signal is unmistakable. The sheep is the hard case and is confused most readily with the baseline, because its "falling silent" resembles the calm nothing. Across all four classes you land near the study's 83 per cent. With your own Biolingo recordings (only two classes) it gets easier — but then it is your plant telling human from emptiness.
The shared timeline
Now the image that gives the chapter its name. Draw the signal energy window by window over time and colour it by who was there. You see the kingdoms side by side — the calm baseline, the restless ups and downs with human and dog, the drop with the sheep.
import matplotlib.pyplot as plt
colour = {"baseline":"#9db3a3", "human":"#6B7A8F", "dog":"#E0A82E", "sheep":"#B5524C"}
plt.figure(figsize=(10, 3.5))
i = 0
for c in CLASSES:
for file in sorted(glob.glob(f"data/{c}/*.wav")):
y, sr = librosa.load(file)
plt.scatter(i, librosa.feature.rms(y=y)[0].mean(), color=colour[c], s=12)
i += 1
plt.xlabel("window (in order)"); plt.ylabel("signal energy")
plt.title("Three kingdoms, one plant, one timeline"); plt.tight_layout(); plt.show()
Worksheet
What connects the kingdoms
- Which two classes does the model separate most surely, which does it confuse most readily? Why exactly these?
- Human and dog raise the variance, the sheep lowers the energy. Formulate a guess why a plant might react differently to a herbivore than to a human.
- List the steps you went through here (signal → … → prediction). Where have you done exactly the same steps before — with the cat, the heartbeat, the voice?
- It is only one basil plant. Name two reasons why the result is exciting but not yet conclusive.
- "The plant reads human and animal." What about this statement is covered by the experiment — and where does over-interpretation begin?
Show solution
1. Most surely it separates human and dog (strong restlessness in the signal); it confuses sheep and baseline most readily, because the sheep's "falling silent" resembles the calm nothing — both are low in energy.
2. Individual. One plausible idea: a herbivore is the real danger to a plant; a "shutting down" might save resources or prepare a chemical defence, while human and dog are more of a general disturbance (more restlessness). What matters is phrasing it as a guess, not a fact.
3. Record signal → cut into windows → build features/spectrogram → let the model decide. Exactly this chain ran with the cat (12.1), the heartbeat (5.1) and the voice (10.1) — a single procedure across all kingdoms. That is the convergence of method.
4. Only one plant (no statement about other specimens/species); no clean control setup, and the measurements partly happened in different places (possible confounders). A pattern in one plant is a start, not a proof.
5. Covered is: from the plant signal one can statistically guess which category of creature was near — better than chance. Over-interpretation begins at "the plant knows/feels/recognises" — what is measured is a correlation between proximity and signal shape, not consciousness.
When it sticks
| Problem | Likely cause & fix |
|---|---|
xgboost will not import | Install first: pip install xgboost. Alternatively RandomForestClassifier from sklearn also works — the study used it for comparison. |
read: 0 windows | Folder structure is wrong. Put the WAVs in data/<class>/ and adjust CLASSES to the folders you actually have. |
| Everything is recognised as "baseline" | Classes strongly imbalanced. Take roughly equal numbers of windows per class or use class_weight/sampling. |
| Very low hits on your own recordings | Too little data or too much interference. Record longer, fix the cables, keep a calm setting — and honestly stay with two clearly separated classes. |
| Very slow | Compute the features once and keep them in X; do not re-read from the WAVs each run. |
Food for thought
- Look closely: it was always the same grip. A voice, a cat sound, a heartbeat, a plant signal — with each you did the same: recorded, turned into an image, let the same machinery read it. A single procedure, from tomato to stock market.
- And deeper: across all kingdoms the same honest signals appear — rhythm, the ups and downs of tension and calm, the return of fixed patterns. On a shared timeline, human, animal and plant move surprisingly close: not because they are alike, but because they are kin in the way they answer their world.
- Keep the boundary here too: what is measured is a correlation between proximity and signal shape — no proof that the plant "knows" who is there. The astonishing need not fear the sober; it holds up to it.
Extension
- Your own kingdoms. Record your own four-class series with the Biolingo sensor: nobody, you, your pet, a second human. Do you reach even close to the study's separating power?
- Temporal shape instead of means. The study tried an LSTM that reads the raw time series instead of averaged features — with a high hit rate for the sheep. Replace the MFCC means with the raw window sequence and compare.
- Confusion matrix. Draw with
sklearn.metrics.confusion_matrixwhich kingdoms are confused most often. Does the sheep, as in the study, fall most readily into the baseline?