Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Градуировка системы сбора температуры на внешнем АЦП

На днях должны подвезти спирт, и я смогу наконец-то подготовить криостат к тестовой откачке и, если течей не будет, испытанию холодом в наших условиях.
А для этого нужно иметь надежную систему сбора температур. Я уже писал о тестировании системы в жидком азоте. Теперь же решил откалибровать при помощи точного омметра и посмотреть, как датчики себя в комнатной температуре будут вести.

Для калибровки я просто повесил вместо терморезисторов один переменный резистор на 1.5кОм и, изменяя его сопротивление, проводил по нескольку замеров. Калибровочные данные получил простым скриптом (по сути, из него нужна только часть внутри if(MF)):
function data_stat(filename, msrd)
% prints some statistics
% also plot graphs

	MF = 0;
	if(nargin == 0) filename = 'tempout'; endif;
	if(nargin == 2) MF = 1; endif;
	D = dlmread(filename);
	if(isempty(D)) return; endif;
	printf("Some statistics:\n\n\trelative error for inner ADC:\n");

	Time = (D(:,1)*2^24+D(:,2))/1000;
	T = []; Rr = [];

	x = rel_error(D(:,[3 4 6:10]));
	printf("%f ", x);
	avrg = mean(x);
	printf("\t(aver: %f)\n", avrg);
	printf("\n\trelative error for outern ADC:\n\n");
	plot(Time, D(:,[3 4 6:10]));
	if(MF)
		R = dlmread(msrd);
		Rr = R(:,2);
		T = R(:,1)/1000;
		addlabels(Time, D(:,[3 4 6:10]), T, Rr);
	endif;
	Tit = sprintf("Internal 12-bit ADC, err=%f%%", avrg);
	xlabel("Time, s"); ylabel("R, ADU"); title(Tit);
	print -dpng -color int.png;
	close;

	x = rel_error(D(:,[11:18]));
	avrg = mean(x);
	printf("%f ", x);
	printf("\t(aver: %f)\n", avrg);
	printf("\n");
	plot(Time, D(:,[11:18]));
	if(MF)
		addlabels(Time, D(:,[11:18]), T, Rr);
	endif;
	Tit = sprintf("External 24-bit ADC, err=%f%%", avrg);
	xlabel("Time, s"); ylabel("R, ADU"); title(Tit);
	print -dpng -color ext.png;
	close

	meanline = mean(D(:,[11:18])')';
	diffs = mean(D(:,[11:18]) - meanline)  % average difference between mean line
	newD  = D(:,[11:18]) - diffs;
	plot(Time, newD);
	if(MF)
		addlabels(Time, newD, T, Rr);
	endif;
	Tit = sprintf("External 24-bit ADC, corrected");
	xlabel("Time, s"); ylabel("R, ADU"); title(Tit);
	print -dpng -color ext_corr.png;
	close
	if(MF)
		% R = K*ADU -> K = ADU \ R
		Kmul = []; Kadd = []; Kmul2 = [];
		for i = 11:18
		dat = interp1(Time, D(:,i), T);
		N = [ ones(size(dat)) dat dat.^2];
		K = N \ Rr;
		Kadd = [Kadd K(1)];
		Kmul = [Kmul K(2)];
		Kmul2= [Kmul2 K(3)];
		endfor
		%Kmul = median(Kmul);
		%Kadd = median(Rr - dat*Kmul);
		printf("coefficients:\n\tKadd = %s\n\tKmul = %s\n\tKmul2 = %s\n", num2str(Kadd), ...
			num2str(Kmul), num2str(Kmul2));
		printf("stat:\n\t = %g, sigma = %g\n\t = %g, sigma = %g\n", ...
			median(Kmul), std(Kmul), median(Kmul2), std(Kmul2));
		dd = D(:,[11:18]);
		newR  = dd.^2 .* Kmul2 + dd .* Kmul + Kadd;
		plot(Time, newR);
		addlabels(Time, newR, T, Rr);
		Tit = sprintf("External 24-bit ADC, corrected");
		xlabel("Time, s"); ylabel("R, Ohm"); title(Tit);
		print -dpng -color ext_corr_Ohm.png;
	endif
endfunction

function addlabels(Time, data, T, Rr)
%
% plot circles with R label
% - Time - original time labels
% - data - original data
% - T    - time labels for Rr
% - Rr   - measured resistance
%
	meanline = mean(data')';
	newDr = interp1(Time, meanline, T);
	%N = [ ones(size(Rr)) Rr Rr.^2];
	N = [ ones(size(Rr)) Rr ];
	K = N \ newDr
	%Y =  Rr.^2 * K(3) + Rr * K(2) + K(1);
	Y =  Rr * K(2) + K(1);
	hold on; plot(T, Y, '.');
	text(T+10, Y, num2str(Rr));
	%plot(Time, meanline, 'o');
	hold off;
endfunction


Сначала я пробовал линейную аппроксимацию, но получилось плохо. А вот квадратичная вполне хорошо легла. Вот что получилось:
calibration_corr_Ohm
Калибровка восьми каналов АЦП. Горизонтальные участки сплошной линии — показания АЦП; точки — измерение омметром.


Так как прецизионного термометра и термостата у меня нет, то, не мудрствуя лукаво, я решил "в лоб" воспользоваться формулой для преобразования сопротивления ТРД HEL-705 в температуру:
function Tout = H705(Rin)
% Converts resistance of TRD into T (degrC)

_alpha = 0.00375;
_beta = 0.16;
_delta = 1.605;
T = [-300:0.1:300];
_A = _alpha + _alpha*_delta/100.;
_B = -_alpha*_delta/1e4;
_C = zeros(size(T));
_C(find(T<0.)) = -_alpha*_beta/1e8;
rT = 1000.*(1 + _A*T + _B*T.^2 - _C.*T.^3*100. + _C.*T.^4);
Tout = interp1(rT, T, Rin, 'spline');
endfunction


Теперь нужно лишь накопить результаты и преобразовать отчеты в температуру:
function [Time T Tfxd] = get_T(filename, filter_treshold)
% convert ADU into T in Kelvins
% filename - name of file with temperature data
% filter_treshold - treshold level

	D = dlmread(filename);
	if(isempty(D)) return; endif;

	Time = (D(:,1)*2^24+D(:,2))/1000;

	% measured coefficients
	Kadd = [35.628     34.595     35.088     34.754     33.936     33.604     33.966     34.816];
	Kmul = [0.00014524 0.00014577  0.0001462 0.00014628 0.00014577 0.00014545 0.00014558 0.00014523];
	Kmul2 = [3.6789e-11  3.688e-11 3.6878e-11 3.6894e-11 3.6923e-11 3.6929e-11 3.6914e-11 3.6841e-11];
	Readed = D(:,[11:18]);
	nstr = [size(Readed, 1) 1];
	Kadd = repmat(Kadd, nstr);
	Kmul = repmat(Kmul, nstr);
	Kmul2 = repmat(Kmul2, nstr);
	R = Readed.^2 .* Kmul2 + Readed .* Kmul + Kadd;
	T = H705(R);

	% filtering
	if(nargin == 2)
		for i = 1:2
			DT = diff(T);
			[r c] = ind2sub(size(DT), find(DT < -filter_treshold));
			T(r+1, :) = [];
			R(r+1, :) = [];
			Time(r+1, :) = [];
		endfor
	endif

	plot(Time, R);
	Tit = sprintf("Resistance, \\Omega");
	xlabel("Time, s"); ylabel("R, \\Omega"); title(Tit);
	print -dpng -color resistance.png;
	close
	plot(Time, T);
	Tit = sprintf("Temperature, ^\\circ{}C");
	xlabel("Time, s"); ylabel("T, ^\\circ{}C"); title(Tit);
	print -dpng -color temperatures.png;
	close

	% calculate difference between thermometers
	Tavr = mean(T, 2);
	Tadd = mean(T - repmat(Tavr, [1 8]));
	Tfxd = T - repmat(Tadd,[size(T,1) 1]);
	plot(Time, Tfxd);
	Tit = sprintf("Temperature fixed to average value, ^\\circ{}C");
	xlabel("Time, s"); ylabel("T, ^\\circ{}C"); title(Tit);
	print -dpng -color temperatures_fixed.png;
	close
	printf("differences:\n"); printf("%g\n", -Tadd);
endfunction


Блок, помеченный как filtering нужен был, пока у меня в "показометре" был баг: периодически терялся 64-байтный буфер, передаваемый по USB. Баг этот я починил (я забыл проверять, ушли ли уже предыдущие данные, прежде чем посылать следующую порцию).

Вот что получается, когда снимаются показания при комнатной температуре:
temperatures
Не скорректированные на общее среднее показания.

Заметно, что ведут себя эти температуры очень скоррелированно, различаясь лишь аддитивно. Поэтому последняя часть предыдущей функции заодно выравнивает данные на общее среднее:
temperatures_fixed
Скорректированные на "общее среднее" температуры.

В пределах пяти сотых получается примерно одно и то же.

Ну и напоследок еще картинка. Я нагрел руками дюралевый стаканчик, в котором находились ТРД:
temperatures_fixed
Без термостата радужной картинки не получается.

Tags: octave, термодатчики
Subscribe

  • Нафиг не нужная поделка

    Зашел на одном из форумов спор, что невозможно поднять (N-1) устройство CDC-ACM на микроконтроллере с N концевыми точками (N<=8). ОК, держите…

  • Велосипедостроение

    Вчера наблюдал на полуметровом телескопе. Теоретически, в случае плохой погоды купол сам должен закрыться, но погода вполне. И, чтобы не ждать до…

  • Очередной терминальный клиент

    Замучившись пересобирать свой простейший терминальный клиент каждый раз, как нужно изменить имя устройства или скорость, решил таки достать из…

promo eddy_em august 17, 2019 12:33 3
Buy for 10 tokens
Юра намедни напечатал корпус для хронометра. Для первого блина получилось неплохо: И еще немного фотографий:
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 0 comments