You are characterizing the ON and OFF thresholds of an event camera with a pulsing light source. The positive curve climbs exactly as the theory says it should; the negative curve wanders, saturates early and refuses to repeat. Before you conclude the sensor has a defective polarity, check the mapping between the bias parameters you are writing and the polarity flags you are plotting — in most benches that mapping is crossed. This guide gives you the convention checks, a five-minute swap test that maps each polarity to its parameter, the event count a flicker stimulus should produce, and the asymmetries that survive a correct mapping. By the end you will know whether to re-label a script or to package recordings for a support claim.
Why do my positive and negative event counts behave nothing alike?
In most bench cases the two curves disagree because the labels are crossed, not because the sensor is asymmetric: the sweep changed one diff bias while the plot showed the other polarity's events, so each curve shows the response of a threshold you were not moving.
The hardware gives both branches nearly symmetric paths — a change amplifier, a comparator and a bias generator per side. Asymmetry this large is a measurement problem until proven otherwise, and exactly two conventions have to line up before a sweep means anything: which diff bias parameter your script writes, and which polarity flag your analysis selects. One textbook curve is itself the clue. It belongs to the threshold you were actually driving; the erratic curve is the other branch wandering uncontrolled through its noise floor.
We treat the value side — what one bias count means in contrast terms — in event camera bias values are not contrast thresholds, and the noise side, which rises steeply as you approach saturation, in event camera bias tuning: cut noise without losing motion.
Which bias parameter actually controls each event polarity?
On most event-based sensors the ON comparator is driven by bias_diff_on and the OFF comparator by bias_diff_off, so the polarity whose count responds to a parameter is the polarity that parameter owns, whatever the plotting tool assumes.
Most SDKs expose the pair under names in that form, one per comparator. The names suggest the mapping, but the layer you drive may not be the layer that ships events: wrappers rename parameters, hide them behind a single contrast slider, or index them from zero. The polarity flag in the stream is a second, independent convention — it can be flipped while the bias names are correct, and the other way round, which is how a bench ends up writing one parameter twice and plotting the wrong branch. The response is the only authority, so the next two sections test the mapping directly instead of trusting a label.
What polarity convention does my event stream use?
Most stacks encode polarity as 0 for OFF and 1 for ON, some use signed values such as -1 and +1, and a wrapper can remap them at its own boundary, so read the value out of the event structure rather than trusting the label in the viewer.
Viewer colors are a hint, not a contract. One physics test settles it. Illuminate a uniform surface, then block the light: a pixel seeing illumination fall releases OFF (negative) events, and the same pixel seeing illumination rise releases ON (positive) events. Whatever flag fires during the darkening step is OFF; whatever fires on re-illumination is ON. The burst timestamps should sit on the stimulus edges — the same alignment discipline described in event camera polarity and timestamps: when events beat frames.
How do I verify the polarity mapping in five minutes?
Drive the sensor with a single slow transition and watch which polarity fires: a controlled flicker at a few hertz separates the branches in one recording, and swapping which bias parameter you move confirms the mapping in two.
- Fix the floor. Aim at a static, uniform surface at a mid-range setting and record 1 s without moving anything. Those two per-polarity counts are the noise floors: a handful to a few hundred events on a 10×10 ROI at a conservative setting, not tens of thousands.
- Flicker the stimulus. Drive a bench LED over the ROI with a 10 Hz square wave at 50% duty. Ten rising and ten falling edges per second read as distinct bursts in a timeline plot.
- Read the bursts. Record 2 s. ON bursts must sit on rising edges and OFF bursts on falling edges, within the pixel's response lag. Bursts on the wrong edges mean the polarity labels are swapped — re-map before touching any bias.
- Nudge one parameter. Raise only bias_diff_on by one sweep step. The polarity whose count moves owns that parameter; a dead branch means the name you wrote never reaches the comparator you think it does.
- Nudge the other. Raise only bias_diff_off by one step. The other polarity should move by a similar margin.
- Write the mapping down. Pair every sweep file with the parameter written and the polarity plotted, and let the script refuse to start when the pair contradicts the mapping you just measured.
How many events should a flicker test produce on a 10×10 ROI?
Multiply the number of illuminated pixels by the number of transitions per second, then check the result against the refractory ceiling: for 100 pixels and a 1 kHz square wave the expectation is 100,000 events per polarity per second, and a branch anywhere near that number is being driven correctly.
The example inputs below are illustrative; the arithmetic is the check.
Set-up: a 10×10 ROI (N_px = 100 pixels) fully covered by the spot, a 1 kHz square wave (f_edge = 1,000 edges per second, per polarity), a 100 µs dead time.
- Half-period: 1 / (2 × 1,000 Hz) = 500 µs. That is 5× the dead time — utilization 100 µs / 500 µs = 20% — so no refractory clipping.
- Per-pixel ceiling: 1 / 100 µs = 10,000 events/s, so the ROI ceiling is 100 × 10,000 = 1,000,000 events/s, ten times the expectation. The ceiling is not what limits you here.
- Expected count per polarity: 100 px × 1,000 edges/s = 100,000 events/s, i.e. 100,000 events in the 1 s recorded at each bias point.
- Partial coverage scales linearly: a spot over 40% of the ROI gives 0.40 × 100,000 = 40,000 events/s per branch.
Read the measurement against those numbers. A positive branch at 98,600 events/s is 98.6% of expectation — ordinary losses at the spot edge and in timing jitter. A branch plotted as negative at 1,150 events/s is 1.2% of expectation: that branch is not being driven by the sweep at all. Either the script writes the same parameter twice, or the polarity filter selects the wrong flag. Fix the mapping, re-run, and both curves should mirror each other up to their own saturation points.
The far end of a sweep deserves its own caution. The values where a camera becomes unresponsive are noise-flood points reached by pushing past the conservative cap — that is what the bias safety override permits — and they make poor operating points. The usable data sits well below them.
What else skews ON and OFF event rates?
After the mapping is verified, five real asymmetries remain, in the order they usually matter: refractory clipping, stimulus geometry, ambient flicker, filter placement and genuine comparator mismatch — and each has a quick bench test.
- Refractory clipping. A dead time close to the half-period eats releases: at 400 µs against a 500 µs half-period, utilization is 400 / 500 = 80%, and timing jitter pushes part of the releases past the next edge, so the branch that fires second clips hardest. A pulse narrower than the dead time is worse: a 10 µs pulse with a 100 µs dead time lets the ON branch fire on the rising edge but swallows the falling edge, so OFF collapses toward zero. Halve the dead time or widen the pulse; converging counts confirm this was the cause.
- Stimulus geometry. If the bright state covers 60% of the ROI and the dark state 35%, geometry alone yields 100 px × 1,000 × 0.60 = 60,000 ON events/s against 35,000 OFF events/s. Lock the spot with a diffuser or fixed aperture and the imbalance disappears.
- Ambient flicker. Mains-driven lighting injects bursts at 100 or 120 Hz into both polarities, and the branch sitting nearest its noise floor shows it most. Take one background run with the stimulus off; that per-polarity floor is the subtraction baseline. The mains-beat mechanisms are the same ones behind camera flicker under artificial lighting: diagnose and fix.
- Filter placement. A temporal filter tuned near the stimulus frequency attenuates real events, and a cut-off sitting between the two branches' timing silences one polarity more than the other. Place that cut-off deliberately; event camera temporal filter bias: can you target a motion frequency works through the choice.
- Comparator mismatch. Real, and modest. It shows up as mirrored curves with different noise floors and different saturation points — never as one flat or erratic branch.
When is the asymmetry real and not a labeling mistake?
Treat the asymmetry as real only when both branches pass the swap test with mirrored curves and the remaining differences survive fixed stimulus geometry and dead time; genuine comparator mismatch shifts the floors and saturation points by a bounded amount, it never leaves one polarity flat.
Different saturation values per polarity are the expected signature of healthy hardware. Since each branch carries its own comparator and bias generator, each reaches noise flood at its own bias value and the camera goes unresponsive there; the two points can sit tens of counts apart, and the gap is device-specific. What stays suspicious is different: one branch that never responds across its entire range, or events that ignore the stimulus transitions entirely. Package a hardware claim with the swap-test evidence — the static-scene floor recording, the flicker recording with both bursts visible, the two single-parameter nudges and the exact parameter names your code wrote. With those attached, a support exchange usually settles the question in one round.
How do I keep polarity mismatches out of the next sweep?
Record both polarities in every run, name each file with the bias parameter you actually changed, and include a null run at a conservative setting; a mismatch then shows up during the sweep as curves that fail to mirror, not after a week of analysis.
- Sweep one diff bias parameter per run, never both — a two-parameter sweep cannot isolate a crossed mapping.
- Log the parameter names from the API calls your code makes, not the names shown in a viewer.
- Take one static-scene recording per session to fix the per-polarity noise floors.
- Take one flicker recording per session, after any SDK, firmware or wrapper update, to re-check the mapping.
- Plot both polarities from the same file before trusting any single-polarity curve.
- Assert in the analysis that the plotted flag matches the parameter swept, and fail loudly when it does not.
If the swap test clears the mapping and both curves still refuse to mirror, send the paired recordings and the parameter names through the contact page. If you are specifying hardware rather than debugging it, the smart cameras category, the camera finder and the camera configurator are the starting points, and the cameras topic page collects the rest of the event-camera checks.
Questions
Does a larger diff bias value make an event camera more or less sensitive?
On most sensors a larger diff bias lowers the contrast threshold, so pixels fire on smaller changes and the noise rate climbs steeply; pushed far enough the pixel floods with noise and the camera stops responding. Confirm the direction on your device with a single controlled step before you quote any sensitivity in bias units.
Why does a static scene still produce events in both polarities?
Temporal noise fires both ways: each fluctuation that crosses whichever threshold is lower produces either an ON or an OFF event, so both counts rise together as you approach the noise flood point. A quiet sensor under a capped lens should sit near zero events per pixel per second; thousands from a fixed scene mean the bias is too hot for measurement.
What dead time should I set for flicker measurements?
Keep the refractory period well inside the shortest interval you want to resolve. For a 1 kHz square wave the 500 µs half-period tolerates a 100 µs dead time with a 5× margin, capping each pixel at 10,000 events/s. Dead time longer than the half-period clips the counts asymmetrically and fakes a polarity imbalance.
Do the ON and OFF branches saturate at the same bias value?
No. Each branch gets its own comparator and bias generator, so each branch reaches noise flood at its own bias value and the camera goes unresponsive there. Measure both flood points once, store them as the device's working envelope, and keep every future sweep below the lower of the two — the useful dynamic range ends well before the unresponsive point.
How do I log both polarities without doubling the sweep time?
Record every event with its polarity flag in one file per bias point and split by polarity during analysis, instead of one recording per polarity. A crossed mapping then announces itself inside the sweep as curves that fail to mirror, and you keep a single timestamp base for both branches, which makes burst-alignment checks exact.