Автоматизація лабораторних вимірювань

Я аспірант інституту металофізики, займаюся дослідженням властивостей різних металів і сплавів. Процес вимірювання і аналізу, як правило трудомісткий і вимагає багато рутинної роботи. У зв'язку з цим була виконана автоматизація установки для дослідження оптичних властивостей металів. Про те, як це було, розповім далі у публікації.



Постановка завдання

Розробити програмне забезпечення для управління підготовкою і проведенням експерименту на автоматизованому оптичному эллипсометре.

Програмне забезпечення повинне надавати можливість
  • завдання початкових установок і параметрів експерименту;
  • проведення вимірювань оптичних характеристик зразків;
  • набір первинних даних [масиви даних I(α) при фіксованій довжині хвилі λ і фіксованому (постійному) значенні величини I(α) у заданому діапазоні довжин хвиль λmin ≤ λ ≤ λmax (де α – азимут аналізатора) або масиви I(λ) при фіксованих азимутах аналізатора α1, α2, α3,… αn];
  • обробку масивів I(α) або I(λ) для обчислення оптичних постійних;
  • управління часом витримки до початку вимірювань в даній точці спектру або азимута аналізатора для вимірювання встановленого рівня сигналу, а також тривалістю «стояння» в даній точці спектру (азимута) для «накопичення» сигналу;
  • візуалізацію вимірювальних даних;
  • збереження даних у файл.


Експеримент

У процесі вимірювання програма повинна керувати кроковими приводами, виконуючи двовимірне сканування. Мінімальний крок сканування залежить від можливостей установки. Кількість точок на діапазон визначається величиною діапазону і кроком сканування.

Параметри експерименту (діапазон вимірювання, крок сканування) в процесі вимірювань не змінюються. Сканування відбувається дискретно, по кроках.

Вимірювання виконується тільки в моменти зупинки приводів. Після виконання вимірювання дається команда переміщення на наступний крок, і проводиться транспортне переміщення. Напруга на ФЕУ регулюється за допомогою ШІМ.

Софт

В якості мови програмування я обрав C#, так як у нього «з коробки» реалізовано багато корисних функцій для роботи з USB, COM-портами, нативна підтримка Microsoft Office і т. д.

Отже, для отримання даних і відправки команд мікроконтролера ми будемо використовувати віртуальний COM-порт:

код
// Open ADC Com
private void buttonADC_Click(object sender, EventArgs e)
{
String s = "";
if (comboBox1.Text != "")
{
s += comboBox1.Text;
}

try
{
hPort = PACNET.UART.Open(s + ",9600,N,8,1");
Log.Items.Add("Порт АЦП відкритий");
buttonADC.Enabled = false;
comboBox1.Enabled = false;
}
catch
{
MessageBox.Show("Порту " + serialPort1.PortName +
"неможливо відкрити!", "Помилка!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Log.Item.add("Неможливо відкрити порт!");
}
}


Для перетворення аналогового сигналу я використовував 16-бітний АЦП фірми ICP Das, для якого на офіційному сайті є SDK, тому читання даних з АЦП тривіальне:

код
float readSequenceADC(int iChannel, int iterations)
{
float result = 0;
for (int i = 0; i < iterations; i++)
{
result += readADC(iChannel);
Thread.Sleep(150);
}
result /= iterations;
return result;
}


Трохи математики, розрахунок оптичних констант:

код
public void count(float a, float b, float c)
{
double x = a;
double y = b;
double z = c;

double ksi = Math.Atan(Math.Sqrt(x / z));
double delta = Math.Acos((x + z - 2 * y) / (2 * Math.Sqrt(x * z)));

double denominator = Math.Pow((1 - Math.Sin(2 * ksi) * Math.Cos(delta)), 2);
double numerator = Math.Pow(Math.Sin(phi), 2) * Math.Pow(Math.Tan(phi), 2) *
Math.Sin(2 * ksi) * Math.Cos(2 * ksi) * Math.Sin(delta);
double nk = numerator / denominator;

double numerator1 = Math.Pow(Math.Cos(2 * ksi), 2) - Math.Pow(Math.Sin(2 * ksi), 2) *
Math.Pow(Math.Sin(delta), 2);
double nk2 = Math.Pow(Math.Sin(phi), 2) + (Math.Pow(Math.Sin(phi), 2) * Math.Pow(Math.Tan(phi), 2) *
(numerator1 / denominator));

double x1 = 0;
double x2 = 0;
double D = nk2 * nk2 - 4 * ((-1) * nk * nk);
if (D < 0)
{
x1 = 0;
x2 = 0;
}
else if (D == 0)
{
x1 = -nk2 / 2;
x2 = -nk2 / 2;
}
else if (D > 0)
{
x1 = (-nk2 + Math.Sqrt(D)) / 2;
x2 = (-nk2 - Math.Sqrt(D)) / 2;
}
if (x1 < 0 && x2 < 0)
{
// "X1, x2 < 0"
}
else if (x1 > 0)
{
k = Math.Sqrt(x1);
}
else if (x2 > 0)
{
k = Math.Sqrt(x2);
}
n = Math.Sqrt(nk2 + k * k);
}


Функція визначення дрейфу нуля сигналу:

код
// Intensity Drifting Check
private void Worker3()
{
while (isWorking == true)
{
try
{
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(200);
I = readADC(3);
Intensity.Add(I);
this.Invoke((MethodInvoker)(() => Log.Items.Add("I = " + I)));
DrawGraph(i, I, 0);
}
PrintExel.ExportText(Intensity);
ClearGraph();
isWorking = false;
}
catch (Exception e) { MessageBox.Show("Error:\n" + e.Message); }
}

}


Фотоелектронний помножувач моторошно нелінійний, тому на різних ділянках спектра у нього різна чутливість і потрібно підтримувати постійний сигнал у встановлених межах (для наших вимірювань це діапазон 300 — 850 мВ при оптимальному співвідношенні сигнал/шум):



код
private void maxControl()
{
double val = 1;
while (val > 0.85)
{
this.radRadialGauge1.Value -= 7;
serialPort1.Write("decrease");
val = Convert.ToInt32(Math.Abs(readADC(3)));
Thread.Sleep(1000);
}
}

private void minControl()
{
double val = 0;
while (val < 0.4) // val < 300
{
this.radRadialGauge1.Value += 7;
serialPort1.Write("increase");
val = Convert.ToInt32(Math.Abs(readADC(3)));
Thread.Sleep(1000);
}
}



Візуалізація даних
Відображення результатів вимірювання в реальному часі — дуже важливий момент, так як дозволяє контролювати процес і правильність виконання експерименту. Проаналізувавши статтю на Хабре «Засоби побудови графіків .NET» обрав ZedGraph. Безкоштовна, швидка, проста і досить функціональна бібліотека.



функція побудови графіків
private void DrawGraph(int x, double n, double k)
{
GraphPane pane = zedGraphControl1.GraphPane;

PointPairList listN = new PointPairList();
PointPairList listK = new PointPairList();
LineItem myCurve;

listN.Add(x, n);
listK.Add(x, k);

myCurve = pane.AddCurve("", listN, Color.Blue, SymbolType.Default);
myCurve = pane.AddCurve("", listK, Color.Red, SymbolType.Default);

zedGraphControl1.AxisChange();
zedGraphControl1.Invalidate();
}



Ну і наостанок, збереження у файл:

код
public static void ExportToExcelIntensity(ArrayList arrLamda, ArrayList arrI0, ArrayList arrI45, ArrayList arrI90)
{
try
{
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Visible = true;
excelApp.Workbooks.Add();
Microsoft.Office.Interop.Excel.Worksheet workSheet = excelApp.ActiveSheet;
workSheet.Cells[1, "A"] = "WaveLength";
workSheet.Cells[1, "B"] = "I0";
workSheet.Cells[1, "С"] = "I45";
workSheet.Cells[1, "D"] = "I90";
int row = 1;

for (int i = 0; i < arrLamda.Count; i++)
{
row++;
workSheet.Cells[row, "A"] = arrLamda[i];
workSheet.Cells[row, "B"] = arrI0[i];
workSheet.Cells[row, "С"] = arrI45[i];
workSheet.Cells[row, "D"] = arrI90[i];
}

workSheet.Range["A1"].AutoFormat(Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2);
excelApp.DisplayAlerts = false;
workSheet.SaveAs(string.Format(@"{0}\OpticalIntensities.xlsx", Environment.CurrentDirectory));
excelApp.Quit();
}
catch (Exception ex)
{
MessageBox.Show("Error:\n" + ex.Message);
}
}



Підсумок
Розроблено Апаратно-програмний комплекс, призначений для автоматизації лабораторних вимірювань, заснований на WinForms. На рахунок продуктивності і актуальності вибору саме цього ЯП для наукових обчислень можна ознайомитися в статті.

P. S. Так виглядає частина керуючого «заліза», про який планую написати в статті:

Фотографія

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.