You are now following this question
- You will see updates in your followed content feed.
- You may receive emails, depending on your communication preferences.
Correcting effects of Humidity on sensors
    59 views (last 30 days)
  
       Show older comments
    
Hi All
I have gas sensor, that gets effected by hummdity that needs to be corrected. So was hoping to see if we can correct this ?
How can i run my code on the this support forum with my data file, so it can be run?
Answers (1)
  Star Strider
      
      
 on 23 Oct 2025 at 14:11
        What sort of correction do you want to do to your data?  
Do you also have the humidity data?  
Are there any published ways to correct the readings for humidity?  If so, please share them.  
To run your code with your data here, first upload the data file, using the 'paperclip' icon in the top toolbar (just to the right of the Σ).  Click on the 'insert a line of doce' icon in the top toolbar (farthest left icon in the CODE section, or ALT+ENTER) to create a code line, then type or copy-paste your code in it.  To run it, press the green arrrow in the top toolbar.  
x = linspace(0, 2*pi);
y = sin(x) .* cos(x);
figure
plot(x, y)
grid
Your code should run here essentially the same way it runs on your computer, including reading the file.  
,
12 Comments
  Dharmesh
 on 23 Oct 2025 at 19:52
				
      Moved: Star Strider
      
      
 on 23 Oct 2025 at 19:57
  
			Thank you for your reply.
As humidity increases and decreases — or due to the rate of change — it introduces transients into my analogue signal. This behaviour is known to occur in electrochemical sensors. I would like to explore whether this effect can be corrected, possibly using a quadratic or adaptive algorithm that could later be implemented or ported into other software code.
If you run my code with my dataset, you’ll notice a clear correlation between humidity and certain variations in the signal. The signal is also affected by temperature, but that influence is much easier to handle, so my current focus is on correcting the humidity-related effects.
%% AAN-803-05 corrections: kT for NO, nT for NO2 & OX (O3)
% Dharmesh – edit the folder if needed:
folder  = 'G:\AVR_Project\MATLAB Projects\Air Sensor\Temp_test';
matFile = fullfile(folder, 'SensorLog_ALL.mat');
% --- Load the combined table ---
S = load(matFile);
fn = fieldnames(S);
T  = S.(fn{find(structfun(@istable, S), 1)});
% --- Time & environment ---
if ~isdatetime(T.Timestamp)
    T.Timestamp = datetime(T.Timestamp,'InputFormat','yyyy-MM-dd HH:mm:ss');
end
time  = T.Timestamp;
TempC = T.("Temperature (°C)");
Hum   = T.("Humidity (%)");
% ================== CALIBRATION CONSTANTS ==================
% ---------- ELECTRONICS OFFSETS (hardware bias; set if measured) ----------
ELEC.NO_WE   = 0.292;  ELEC.NO_AUX   = 0.259;
ELEC.OX_WE   = 0.229;  ELEC.OX_AUX   = 0.227;   % OX uses O3 columns
ELEC.NO2_WE  = 0.238;  ELEC.NO2_AUX  = 0.225;
% ---------- SENSOR ZERO BASELINES (clean air, include electronics if measured that way) ----------
ZERO.NO_WE   = 0.3158;  ZERO.NO_AUX   = 0.3017;   % NO-B4
ZERO.OX_WE   = 0.2366;  ZERO.OX_AUX   = 0.2278;   % OX-A431 (O3)
ZERO.NO2_WE  = 0.2396;  ZERO.NO2_AUX  = 0.2253;   % NO2-B43F
% ===========================================================
% --- Ensure expected columns exist (adjust here if names differ) ---
need = ["NO WE","NO AUX","O3 WE","O3 AUX","NO2 WE","NO2 AUX"];
have = string(T.Properties.VariableNames);
if ~all(ismember(need, have))
    error('Missing columns. Expected: %s\nHave: %s', strjoin(need,', '), strjoin(have,', '));
end
% --- Initialise structs to avoid dot-indexing errors ---
WE = struct(); AE = struct(); WEo = struct(); AEo = struct();
% --- Convert zeros to sensor-only baselines (remove electronics bias) ---
WEo.NO  = ZERO.NO_WE  - ELEC.NO_WE;   AEo.NO  = ZERO.NO_AUX  - ELEC.NO_AUX;
WEo.OX  = ZERO.OX_WE  - ELEC.OX_WE;   AEo.OX  = ZERO.OX_AUX  - ELEC.OX_AUX;
WEo.NO2 = ZERO.NO2_WE - ELEC.NO2_WE;  AEo.NO2 = ZERO.NO2_AUX - ELEC.NO2_AUX;
% --- Raw -> electronics-corrected live readings ---
WE.NO   = T.("NO WE")   - ELEC.NO_WE;    AE.NO   = T.("NO AUX")  - ELEC.NO_AUX;
WE.OX   = T.("O3 WE")   - ELEC.OX_WE;    AE.OX   = T.("O3 AUX")  - ELEC.OX_AUX;
WE.NO2  = T.("NO2 WE")  - ELEC.NO2_WE;   AE.NO2  = T.("NO2 AUX") - ELEC.NO2_AUX;
% ---------------- Temperature compensation tables ----------------
T_pts   = [-30 -20 -10  0  10  20  30  40  50];
% NO (kT, ratio model)
kT_NO   = interp1(T_pts, [1.8 1.8 1.4 1.1 1.1 1.0 0.9 0.9 0.8], TempC, 'linear','extrap');
% NO2 & OX (nT, simple model) – replace OX row with your own if you have it
nT_NO2  = interp1(T_pts, [1.3 1.3 1.3 1.3 1.0 0.6 0.4 0.2 -1.5], TempC, 'linear','extrap');
nT_OX   = interp1(T_pts, [1.3 1.3 1.3 1.3 1.0 0.6 0.4 0.2 -1.5], TempC, 'linear','extrap');
% ---------------- Apply algorithms ----------------
% NO (Algorithm 2 / ratio): WEc = (WEu - WEe) - kT*(WEo/AEo)*(AEu - AEe)
WEc_NO   = (WE.NO) - kT_NO .* (ZERO.NO_WE ./ZERO.NO_AUX ) .* (AE.NO );
% NO2 (Algorithm 1 / nT):   WEc = (WEu - WEe) - nT*(AEu - AEe)
%WEc_NO2  = (WE.NO2 - WEo.NO2) - nT_NO2 .* (AE.NO2 - AEo.NO2);
WEc_NO2  = (WE.NO2) - nT_NO2 .* (AE.NO2);
% OX (Algorithm 1 / nT):    WEc = (WEu - WEe) - nT*(AEu - AEe)
WEc_OX   = (WE.OX ) - nT_OX  .* (AE.OX );
% ---------------- Plot sets ----------------
makeFig("NO (k_T ratio)",  time, TempC, Hum, T.("NO WE") ,  T.("NO AUX"),  WEc_NO);
makeFig("NO2 (n_T simple)",time, TempC, Hum, T.("NO2 WE") , T.("NO2 AUX"), WEc_NO2);
makeFig("OX/O3 (n_T simple)",time,TempC, Hum, T.("O3 WE") ,  T.("O3 AUX"),  WEc_OX);
%% --------------- Plot helper ---------------
function makeFig(name,t,tc,hum,WE,AUX,WEc)
f = figure('Name',name,'Color','w');
tl = tiledlayout(f,2,1,'TileSpacing','compact','Padding','compact');
% Top: Temperature & Humidity
ax1 = nexttile(tl);
yyaxis left,  plot(t,tc,'LineWidth',1.2), ylabel('Temperature (°C)')
yyaxis right, plot(t,hum,'LineWidth',1.2), ylabel('Humidity (%)')
grid on, title([name,' — Temp & Humidity'])
% Bottom: WE/AUX vs Corrected
ax2 = nexttile(tl);
yyaxis left
plot(t,WE,'LineWidth',1.2); hold on
plot(t,AUX,'LineWidth',1.2); ylabel('Raw Sensor (V)')
yyaxis right
plot(t,WEc,'LineWidth',1.5); ylabel('Corrected WE_c (V)')
xlabel('Time'), grid on
title([name,' — WE/AUX (left), WE_c (right)'])
legend({'WE','AUX','WE Corrected*'},'Location','best')
linkaxes([ax1 ax2],'x');
end
  Star Strider
      
      
 on 23 Oct 2025 at 20:04
				My pleasure!  
You provided the .m file for your code, however not the data ('SensorLog_ALL.mat').  (I checked -- the data are not included., at least that I could see.)  
It would help to have it.  
  Dharmesh
 on 23 Oct 2025 at 20:18
				
      Edited: Torsten
      
      
 on 23 Oct 2025 at 20:22
  
			Sorry its attached now
%% AAN-803-05 corrections: kT for NO, nT for NO2 & OX (O3)
% Dharmesh – edit the folder if needed:
%folder  = 'G:\AVR_Project\MATLAB Projects\Air Sensor\Temp_test';
matFile = 'SensorLog_ALL.mat';
% --- Load the combined table ---
S = load(matFile);
fn = fieldnames(S);
T  = S.(fn{find(structfun(@istable, S), 1)});
% --- Time & environment ---
if ~isdatetime(T.Timestamp)
    T.Timestamp = datetime(T.Timestamp,'InputFormat','yyyy-MM-dd HH:mm:ss');
end
time  = T.Timestamp;
TempC = T.("Temperature (°C)");
Hum   = T.("Humidity (%)");
% ================== CALIBRATION CONSTANTS ==================
% ---------- ELECTRONICS OFFSETS (hardware bias; set if measured) ----------
ELEC.NO_WE   = 0.292;  ELEC.NO_AUX   = 0.259;
ELEC.OX_WE   = 0.229;  ELEC.OX_AUX   = 0.227;   % OX uses O3 columns
ELEC.NO2_WE  = 0.238;  ELEC.NO2_AUX  = 0.225;
% ---------- SENSOR ZERO BASELINES (clean air, include electronics if measured that way) ----------
ZERO.NO_WE   = 0.3158;  ZERO.NO_AUX   = 0.3017;   % NO-B4
ZERO.OX_WE   = 0.2366;  ZERO.OX_AUX   = 0.2278;   % OX-A431 (O3)
ZERO.NO2_WE  = 0.2396;  ZERO.NO2_AUX  = 0.2253;   % NO2-B43F
% ===========================================================
% --- Ensure expected columns exist (adjust here if names differ) ---
need = ["NO WE","NO AUX","O3 WE","O3 AUX","NO2 WE","NO2 AUX"];
have = string(T.Properties.VariableNames);
if ~all(ismember(need, have))
    error('Missing columns. Expected: %s\nHave: %s', strjoin(need,', '), strjoin(have,', '));
end
% --- Initialise structs to avoid dot-indexing errors ---
WE = struct(); AE = struct(); WEo = struct(); AEo = struct();
% --- Convert zeros to sensor-only baselines (remove electronics bias) ---
WEo.NO  = ZERO.NO_WE  - ELEC.NO_WE;   AEo.NO  = ZERO.NO_AUX  - ELEC.NO_AUX;
WEo.OX  = ZERO.OX_WE  - ELEC.OX_WE;   AEo.OX  = ZERO.OX_AUX  - ELEC.OX_AUX;
WEo.NO2 = ZERO.NO2_WE - ELEC.NO2_WE;  AEo.NO2 = ZERO.NO2_AUX - ELEC.NO2_AUX;
% --- Raw -> electronics-corrected live readings ---
WE.NO   = T.("NO WE")   - ELEC.NO_WE;    AE.NO   = T.("NO AUX")  - ELEC.NO_AUX;
WE.OX   = T.("O3 WE")   - ELEC.OX_WE;    AE.OX   = T.("O3 AUX")  - ELEC.OX_AUX;
WE.NO2  = T.("NO2 WE")  - ELEC.NO2_WE;   AE.NO2  = T.("NO2 AUX") - ELEC.NO2_AUX;
% ---------------- Temperature compensation tables ----------------
T_pts   = [-30 -20 -10  0  10  20  30  40  50];
% NO (kT, ratio model)
kT_NO   = interp1(T_pts, [1.8 1.8 1.4 1.1 1.1 1.0 0.9 0.9 0.8], TempC, 'linear','extrap');
% NO2 & OX (nT, simple model) – replace OX row with your own if you have it
nT_NO2  = interp1(T_pts, [1.3 1.3 1.3 1.3 1.0 0.6 0.4 0.2 -1.5], TempC, 'linear','extrap');
nT_OX   = interp1(T_pts, [1.3 1.3 1.3 1.3 1.0 0.6 0.4 0.2 -1.5], TempC, 'linear','extrap');
% ---------------- Apply algorithms ----------------
% NO (Algorithm 2 / ratio): WEc = (WEu - WEe) - kT*(WEo/AEo)*(AEu - AEe)
WEc_NO   = (WE.NO) - kT_NO .* (ZERO.NO_WE ./ZERO.NO_AUX ) .* (AE.NO );
% NO2 (Algorithm 1 / nT):   WEc = (WEu - WEe) - nT*(AEu - AEe)
%WEc_NO2  = (WE.NO2 - WEo.NO2) - nT_NO2 .* (AE.NO2 - AEo.NO2);
WEc_NO2  = (WE.NO2) - nT_NO2 .* (AE.NO2);
% OX (Algorithm 1 / nT):    WEc = (WEu - WEe) - nT*(AEu - AEe)
WEc_OX   = (WE.OX ) - nT_OX  .* (AE.OX );
% ---------------- Plot sets ----------------
makeFig("NO (k_T ratio)",  time, TempC, Hum, T.("NO WE") ,  T.("NO AUX"),  WEc_NO);

makeFig("NO2 (n_T simple)",time, TempC, Hum, T.("NO2 WE") , T.("NO2 AUX"), WEc_NO2);

●
makeFig("OX/O3 (n_T simple)",time,TempC, Hum, T.("O3 WE") ,  T.("O3 AUX"),  WEc_OX);

%% --------------- Plot helper ---------------
function makeFig(name,t,tc,hum,WE,AUX,WEc)
f = figure('Name',name,'Color','w');
tl = tiledlayout(f,2,1,'TileSpacing','compact','Padding','compact');
% Top: Temperature & Humidity
ax1 = nexttile(tl);
yyaxis left,  plot(t,tc,'LineWidth',1.2), ylabel('Temperature (°C)')
yyaxis right, plot(t,hum,'LineWidth',1.2), ylabel('Humidity (%)')
grid on, title([name,' — Temp & Humidity'])
% Bottom: WE/AUX vs Corrected
ax2 = nexttile(tl);
yyaxis left
plot(t,WE,'LineWidth',1.2); hold on
plot(t,AUX,'LineWidth',1.2); ylabel('Raw Sensor (V)')
yyaxis right
plot(t,WEc,'LineWidth',1.5); ylabel('Corrected WE_c (V)')
xlabel('Time'), grid on
title([name,' — WE/AUX (left), WE_c (right)'])
legend({'WE','AUX','WE Corrected*'},'Location','best')
linkaxes([ax1 ax2],'x');
end
  Star Strider
      
      
 on 23 Oct 2025 at 21:07
				
      Edited: Star Strider
      
      
 on 24 Oct 2025 at 0:44
  
			Torsten -- Thank you!  
@Dharmesh -- I am having a bit of trouble understnading this.  
What are the various variables?  I do not completely understand 'WE" and 'AUX'.  
Do you have the actual NOx values?  (Is this a calibration test?)  
I do not understand the technology of the sensor, and this may be necessary to devise a way of correcting its readings.  Do you have a published way of correcting the sensor values for the humidity?  Is there a specific way that humidity interferes with the sensor (does it 'poison' it, physically block it, or something else)?  
EDIT -- (24 Oct 2025 at 00:45)
See if the sensors dexcribed in HUMIDITY AND TEMPERATURE CORRECTION FACTORS FOR NOX EMISSIONS FROM DIESEL ENGINES matches your sensor.  If so, we can probably use one of these correction approaches.  Those appear to be relatively straightforward, however it will be necessary to state the units of your data, and make any necessary unit conversions to work with these equations.  Specifically, Equation (4) would be easy to code.  
.
  Dharmesh
 on 24 Oct 2025 at 8:12
				Sorry. , the sensor outputs two signals: WE and AUX. The difference between them is that WE includes the gas concentration, while AUX does not. The purpose of this is so that AUX can be used to identify whether any noise or environmental factors have affected the sensor.
However, both signals are influenced by temperature and humidity. Temperature is relatively simple to correct  you ensure that the gas concentration is zero (or very close to zero) and then create a baseline by measuring at various temperature steps.
In this case, we can assume that the gas concentration is close to zero. At this stage, we do not need to calibrate the concentration.
The signal we need to focus on is the corrected WE signal, shown in red. Primarily, you can see humidity transients, which simply affect the current and therefore the final voltage.
There is some information on these sensors in the application note, please see the last page:
Sensor data sheet for NO2. All other sensors would in principle be similar
  Star Strider
      
      
 on 24 Oct 2025 at 19:37
				I am still having problems understanding this.  
I thought this would be more straightforward than it turned out to be.  (I have a chemistry background, however this is far ffrom my areas of expertise.)  
I looked up and implemented Equation (3) from the paper I cited in my previous Comment.  (There actually is not nuch information on this, at least that I can find.)  I wrote a simple function for the formula that appeared to be most applicable, however the absolute humidity units (this formula gives g/m^3, while the equations in the paper require g/kg, so that may require an additional calculation) I then did an example calculation using observed data from the first two subplots for the temperatire, relative humidity, and NO concentration to calculate a 'corrected' NO value.  Those are at the end of the code block.  
This is the best that I can do.  I provided a sample calculation and plots of although I did not apply the correction to the data.
 although I did not apply the correction to the data.  
 although I did not apply the correction to the data.
 although I did not apply the correction to the data.  %% AAN-803-05 corrections: kT for NO, nT for NO2 & OX (O3)
% Dharmesh – edit the folder if needed:
%folder  = 'G:\AVR_Project\MATLAB Projects\Air Sensor\Temp_test';
matFile = 'SensorLog_ALL.mat';
% --- Load the combined table ---
S = load(matFile);
fn = fieldnames(S);
T  = S.(fn{find(structfun(@istable, S), 1)});
% --- Time & environment ---
if ~isdatetime(T.Timestamp)
    T.Timestamp = datetime(T.Timestamp,'InputFormat','yyyy-MM-dd HH:mm:ss');
end
time  = T.Timestamp;
TempC = T.("Temperature (°C)");
Hum   = T.("Humidity (%)");
% ================== CALIBRATION CONSTANTS ==================
% ---------- ELECTRONICS OFFSETS (hardware bias; set if measured) ----------
ELEC.NO_WE   = 0.292;  ELEC.NO_AUX   = 0.259;
ELEC.OX_WE   = 0.229;  ELEC.OX_AUX   = 0.227;   % OX uses O3 columns
ELEC.NO2_WE  = 0.238;  ELEC.NO2_AUX  = 0.225;
% ---------- SENSOR ZERO BASELINES (clean air, include electronics if measured that way) ----------
ZERO.NO_WE   = 0.3158;  ZERO.NO_AUX   = 0.3017;   % NO-B4
ZERO.OX_WE   = 0.2366;  ZERO.OX_AUX   = 0.2278;   % OX-A431 (O3)
ZERO.NO2_WE  = 0.2396;  ZERO.NO2_AUX  = 0.2253;   % NO2-B43F
% ===========================================================
% --- Ensure expected columns exist (adjust here if names differ) ---
need = ["NO WE","NO AUX","O3 WE","O3 AUX","NO2 WE","NO2 AUX"];
have = string(T.Properties.VariableNames);
if ~all(ismember(need, have))
    error('Missing columns. Expected: %s\nHave: %s', strjoin(need,', '), strjoin(have,', '));
end
% --- Initialise structs to avoid dot-indexing errors ---
WE = struct(); AE = struct(); WEo = struct(); AEo = struct();
% --- Convert zeros to sensor-only baselines (remove electronics bias) ---
WEo.NO  = ZERO.NO_WE  - ELEC.NO_WE;   AEo.NO  = ZERO.NO_AUX  - ELEC.NO_AUX;
WEo.OX  = ZERO.OX_WE  - ELEC.OX_WE;   AEo.OX  = ZERO.OX_AUX  - ELEC.OX_AUX;
WEo.NO2 = ZERO.NO2_WE - ELEC.NO2_WE;  AEo.NO2 = ZERO.NO2_AUX - ELEC.NO2_AUX;
% --- Raw -> electronics-corrected live readings ---
WE.NO   = T.("NO WE")   - ELEC.NO_WE;    AE.NO   = T.("NO AUX")  - ELEC.NO_AUX;
WE.OX   = T.("O3 WE")   - ELEC.OX_WE;    AE.OX   = T.("O3 AUX")  - ELEC.OX_AUX;
WE.NO2  = T.("NO2 WE")  - ELEC.NO2_WE;   AE.NO2  = T.("NO2 AUX") - ELEC.NO2_AUX;
% ---------------- Temperature compensation tables ----------------
T_pts   = [-30 -20 -10  0  10  20  30  40  50];
% NO (kT, ratio model)
kT_NO   = interp1(T_pts, [1.8 1.8 1.4 1.1 1.1 1.0 0.9 0.9 0.8], TempC, 'linear','extrap');
% NO2 & OX (nT, simple model) – replace OX row with your own if you have it
nT_NO2  = interp1(T_pts, [1.3 1.3 1.3 1.3 1.0 0.6 0.4 0.2 -1.5], TempC, 'linear','extrap');
nT_OX   = interp1(T_pts, [1.3 1.3 1.3 1.3 1.0 0.6 0.4 0.2 -1.5], TempC, 'linear','extrap');
% ---------------- Apply algorithms ----------------
% NO (Algorithm 2 / ratio): WEc = (WEu - WEe) - kT*(WEo/AEo)*(AEu - AEe)
WEc_NO   = (WE.NO) - kT_NO .* (ZERO.NO_WE ./ZERO.NO_AUX ) .* (AE.NO );
% NO2 (Algorithm 1 / nT):   WEc = (WEu - WEe) - nT*(AEu - AEe)
%WEc_NO2  = (WE.NO2 - WEo.NO2) - nT_NO2 .* (AE.NO2 - AEo.NO2);
WEc_NO2  = (WE.NO2) - nT_NO2 .* (AE.NO2);
% OX (Algorithm 1 / nT):    WEc = (WEu - WEe) - nT*(AEu - AEe)
WEc_OX   = (WE.OX ) - nT_OX  .* (AE.OX );
% ---------------- Plot sets ----------------
makeFig("NO (k_T ratio)",  time, TempC, Hum, T.("NO WE") ,  T.("NO AUX"),  WEc_NO);

makeFig("NO2 (n_T simple)",time, TempC, Hum, T.("NO2 WE") , T.("NO2 AUX"), WEc_NO2);

makeFig("OX/O3 (n_T simple)",time,TempC, Hum, T.("O3 WE") ,  T.("O3 AUX"),  WEc_OX);

●
%% --------------- Plot helper ---------------
function makeFig(name,t,tc,hum,WE,AUX,WEc)
f = figure('Name',name,'Color','w');
tl = tiledlayout(f,2,1,'TileSpacing','compact','Padding','compact');
% Top: Temperature & Humidity
ax1 = nexttile(tl);
yyaxis left,  plot(t,tc,'LineWidth',1.2), ylabel('Temperature (°C)')
yyaxis right, plot(t,hum,'LineWidth',1.2), ylabel('Humidity (%)')
grid on, title([name,' — Temp & Humidity'])
% Bottom: WE/AUX vs Corrected
ax2 = nexttile(tl);
yyaxis left
plot(t,WE,'LineWidth',1.2); hold on
plot(t,AUX,'LineWidth',1.2); ylabel('Raw Sensor (V)')
yyaxis right
plot(t,WEc,'LineWidth',1.5); ylabel('Corrected WE_c (V)')
xlabel('Time'), grid on
title([name,' — WE/AUX (left), WE_c (right)'])
legend({'WE','AUX','WE Corrected*'},'Location','best')
linkaxes([ax1 ax2],'x');
end
KNOx = Fritz(40,80)                     % Calculated Correction Factor
KNOx = 0.5042
NOcorr = [0.45; 0.35]*KNOx              % Corrected Values
NOcorr = 2×1
    0.2269
    0.1765
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
KNOxv = Fritz(TempC, Hum);              % Correctio9ns Vector Correspoinding TO Top Plot In Each Figure
figure
plot(time, KNOxv)
grid
xlabel('Time')
ylabel('KNO_x')
title('KNO_x Correction Factor')

figure
plot3(TempC, Hum, KNOxv)
grid
xlabel('T (ºC)')
ylabel('Relative Humidity (%)')
zlabel('KNO_x')
title('KNO_x Correction Factor')

function KNOx = Fritz(T,RH)
% Source KNOx: https://wiki.unece.org/download/attachments/51972522/EPPR-21-17%20Humidity%20and%20Temperature%20correction%20factors%20for%20NOx%20from%20diesel.pdf?api=v2
% T = Ambient Temperature ºC
% RH = Relative Humidity %
% Source Habs: https://www.reddit.com/r/embedded/comments/f4iino/whats_the_right_way_to_convert_relative_humidity/
Habs = 216.7*(RH/100 .* 6.112 .* exp((17.62.*T)./(243.12+T)))./(273.15+T);  %Units: g/m^3
KNOx = 1+(0.00446*(T-25) - 0.018708*(Habs-10.71)); % Source KNOx Eqn (3)
end
.
  Star Strider
      
      
 on 24 Oct 2025 at 20:52
				My pleasure!  
This is the only correction that I can find that makes any sense.  I noticed in the application notes that humidity changes cause transients with exponential decaay characteristics back to baseline.  I have no way of modeling that, because I do not understand the sensor technoloby thoroughly enough.  It would be necessary to do that experiment with your sensor and then create the appropriate regression using the input-output characteristics.  Apparently only the transients are significant, and the sensor seems to correct itself to the ambient humidity otherwise.  
If this is not what you want, then I will delete my answer.  
  Dharmesh
 on 26 Oct 2025 at 14:23
				
      Edited: Dharmesh
 on 26 Oct 2025 at 14:24
  
			Yes, what you’ve observed is correct  as humidity changes, there is an exponential delay in returning to the baseline. Humidity transients are more noticeable in NO₂ and OX sensors, while NO sensors do not appear to be affected as much. I will ask the manufacturer why this occurs.
Here’s a small idea  please advise if it’s feasible.
From simple observation, I can estimate what the baseline signal should be, assuming the humidity remains stable. At this stage, it might not be 100% accurate, but it could, in principle, generate a signal representing temperatures from 10°C to 45°C. This could be implemented as a simple lookup table.
We could then use this generated signal as a reference for what we expect the signal to be, and compare it with the actual WE signal using a model ,perhaps with the Regression Learner app.
If this approach works, we need to consider how to include humidity in the model. I don’t think relative humidity (RH) alone would be sufficient; instead, we may need to look at the rate of change (± humidity variation) over a certain duration. We can test different time windows  for example, 1 min, 5 min, and 10 min . Some times humdity could be increasing slowly throught out the day.
Another important consideration is that the model should not rely on an absolute reference to the baseline. If a sensor has a different baseline, the correction should still work. Therefore, the correction factor should be expressed as a ± percentage relative to its baseline.
  Star Strider
      
      
 on 26 Oct 2025 at 15:25
				The 'lookup table' coould be implemented with one of the interpolation functions in core MATLAB.  That part would not be difficult.  
I have also been thinking about this with respect to the humidity transients, since that seems to be the most important problem.  
Looking at the 'Humidity Transients' plot in 'App_note_v0', one option would be to re-create that experiment with numidity 'steps' and record the output.  I am not certain what the sampling frequency would be, however considering that the note mentions that the numidity transients 'decay in about 10 minutes', sampling at 1 Hz might be enough, although more data is always better, so use a higher sampling frequency if system memory permits.    Then use the System Identification Toolbox to derive the system structure (most likely a state space representation) choosing the appropriate order and using the compare function to determine the best system order.  With that information, you can use the recorded humidity as an input, calculate the resulting output (simulate the state space representation with the recorded humidity signal), and then subtract that output signal from the observed result.  That is the only way I can think of to correct for the transients.  I doubt that any other approach would work.  
The 'Humidity Transients' plot does not report either the input signal (probably some sort of step-wise humidity change) or the time, so using that plot as it exists would not be appropriate.  (Without knowing the precise input signal and its shape, and the associated times, that plot is quantitatively useless.)  It would be necessary to duplicate that experiment and mathematically identify the system in order to model the sensor response to humidity.  
I can help with the system identification process once you have those data.  
.
  Dharmesh
 ungefär 3 timmar ago
				
      Moved: Torsten
      
      
 ungefär 3 timmar ago
  
			@Star Strider I’ve collected new and improved data, please see below. I used an environmental chamber, which allowed me to stabilise the humidity much better than before.
Before I present my code and data for us to work on the humidity model, I’d like to clean up the data a little so we can present a reference signal that represents the expected behaviour.
The areas that need cleaning are as follows:
- Start and End: These sections correspond to when I started and stopped the test, during which the machine was powering up or shutting down. I can simply remove these timestamps from the data file.
- Temperature Steps: Each time the temperature increases, it causes a step change, and the humidity shifts slightly, creating small transients in the signal. After each temperature step, I wait for 10 minutes before moving to the next step. Therefore, I’d like to discard the step period and the first 5 minutes following each step.
Is there a function that can do this automatically instead of manually removing the data?

  Star Strider
      
      
 ungefär en timme ago
				I would not eliminate or edit anything.  The transients in the step changes are important.  If you know the inpuits (specifically the temperature changes and whatever else was the input signal, such as humidity changes), and have that recorrd, that is all that would be necessary.  The input signal needs to be provided as part of the record.  I would include the start and end, unless the instrumentation was just coming online and was not stable in those regions.  
I am not certain how the humidity changes with temperature, although that might be automatically included in the estimated system.  It is linear, (or at least linear  in the parameters) in the region-of-interest, that would be enough.  
I am still not certain what you are doing, however the data appear to be good.  
The parameter estimation will model the system put to it in the data.  If you are simply interested in humidity transients, you need to increase, decresas, (or both) the humidity, record those changes in the input signal, and record the system output.  Temperature variations will model temperature.  Everything else is inference.  
See Also
Categories
				Find more on AI for Signals in Help Center and File Exchange
			
	Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!An Error Occurred
Unable to complete the action because of changes made to the page. Reload the page to see its updated state.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom(English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)


