Here you can read more detailed information about the sensor itself, and in the article Using the C-IF-6400R sensor to detect the position of people you can read one of the examples of using the sensor.
This article will describe a program for an example of using a sensor with another way of processing data from sensors, from which an averaged temperature map is then created, or using the sensor to count people who entered or left the room.
1 prgMain
Data processing from the sensor, averaging the temperature map and counting people passed is each implemented as its own function block, which are called in the main program prgMain.
VAR_GLOBAL RETAIN
count_people_from_left_to_right : BOOL := 0;
count_people_from_right_to_left : BOOL := 1;
SamplingPeriod : TIME := T#10s;
temperature_matrix_average : ARRAY [1..64] of REAL;
n : UDINT;
people_inside_now : INT := 0;
sum_people_in : INT := 0;
sum_people_out : INT := 0;
END_VAR
PROGRAM prgMain
VAR
GridEye : fbGridEye;
//temperature_trigger_difference : REAL := 2.0;
temperature_trigger : REAL := 20.0;
temperature_offset : REAL := 0.0;
i : INT;
SamplingTimer : TON;
people_counter_fb : fbPeopleCounter;
END_VAR
// The GridEye function block needs the whole DATA structure from the sensor C-IF-6400R passed to the input sensor_data
// The function block has three output arrays/matrices :
// the temperature difference matrix gives 64 values of temp. diff. to the reference temperature for each point
// the temperature matrix gives 64 values of measured temperatures for each point, temperature at each point is calculated as reference + temp. difference
// the presence_matrix gives 64 BOOL values, each point activates when the temperature difference at such point is higher than the defined temperature_trigger_difference in the VAR_INPUT
GridEye(
sensor_data := r4144_p0_IN.DATA,
temperature_trigger := temperature_trigger,
temperature_offset := temperature_offset);
// Averaging the heat map
SamplingTimer(
IN := NOT SamplingTimer.Q,
PT := SamplingPeriod);
IF SamplingTimer.Q THEN
FOR i := 1 to 64 DO
IF n > 0 THEN
temperature_matrix_average[i] := fAverage(
mean := temperature_matrix_average[i],
NewValue := GridEye.temperature_matrix[i],
n := n);
END_IF;
END_FOR;
n := n+1;
END_IF;
// People counting
people_counter_fb(
left_to_right := count_people_from_left_to_right,
right_to_left := count_people_from_right_to_left,
right_side_active := GridEye.right_side_active,
center_horizontal_active := GridEye.center_horizontal_active,
left_side_active := GridEye.left_side_active);
people_inside_now := people_counter_fb.people_counter;
sum_people_in := people_counter_fb.people_in;
sum_people_out := people_counter_fb.people_out;
END_PROGRAM
1.1 Data processing from the sensor
Data processing from the sensor takes place in the function block fbGridEye whose source code is as follows:
FUNCTION_BLOCK fbGridEye
VAR_INPUT
sensor_data : TCIB_GRIDEYE_DATA; // The GridEye function block needs the whole DATA structure from the sensor C-IF-6400R passed to the input sensor_data
//temperature_trigger_difference : REAL := 4.0;
temperature_offset : REAL := 0.0;
temperature_trigger : REAL := 22.0;
END_VAR
VAR_OUTPUT
// The function block has three output arrays/matrices :
// the temperature difference matrix gives 64 values of temp. diff. to the reference temperature for each point
// the temperature matrix gives 64 values of measured temperatures for each point, temperature at each point is calculated as reference + temp. difference
// the presence_matrix gives 64 BOOL values, each point activates when the temperature difference at such point is higher than the defined temperature_trigger_difference in the VAR_INPUT
temperature_difference_matrix : ARRAY [1..64] of REAL;
temperature_matrix : ARRAY [1..64] of REAL;
presence_matrix : ARRAY [1..64] of BOOL;
left_side_active : BOOL := 0;
center_horizontal_active : BOOL := 0;
right_side_active : BOOL := 0;
top_active : BOOL := 0;
center_vertical_active : BOOL := 0;
bottom_active : BOOL := 0;
END_VAR
VAR_IN_OUT
END_VAR
VAR
index_matrix : INT;
index : USINT;
ref : REAL;
temp : TCIB_GRIDEYE_TEMP;
column : INT;
row : INT;
i : INT;
j : INT;
END_VAR
VAR_TEMP
END_VAR
index := sensor_data.INDEX;
ref := sensor_data.REF;
temp := sensor_data.TEMP;
right_side_active := 0;
center_horizontal_active := 0;
left_side_active := 0;
top_active := 0;
center_vertical_active := 0;
bottom_active := 0;
IF index > 0 THEN
FOR i := 1 to 8 DO
index_matrix := ((USINT_TO_INT(index))-1)*8 + i;
temperature_matrix[index_matrix] := SINT_TO_REAL(temp[8-i])/10.0 + ref + temperature_offset;
temperature_difference_matrix[index_matrix] := SINT_TO_REAL(temp[8-i])/10.0;
END_FOR;
END_IF;
FOR j := 1 to 64 do
// Finding points where temperature difference is higher than the defined trigger value
(*
IF temperature_difference_matrix[j] >= temperature_trigger_difference then
presence_matrix[j] := 1;
ELSIF temperature_difference_matrix[j] < temperature_trigger_difference then
presence_matrix[j] := 0;
END_IF;
*)
// Finding point where measured temperature is higher than the defined trigger temperature
IF temperature_matrix[j] >= temperature_trigger then
presence_matrix[j] := 1;
ELSIF temperature_matrix[j] < temperature_trigger then
presence_matrix[j] := 0;
END_IF;
// When a point in the presence matrix is 1 then the column number to which the point belongs is found
// This column is used to calculate which of the three zones the active point belongs to
IF presence_matrix[j] THEN
// Column and row can be calculated via MOD and DIV for a zero-based indexing so a 1 needs to be added after the calculation of MOD and DIV
column := MOD(IN1 := j-1, IN2 := 8) + 1; // The function MOD gives the integer remainder of division which corresponds to the number of the column - only 0 means the 8th column
row := DIV(IN1 := j-1, IN2 := 8) + 1; // Row is calculated from the index number divided by the number of columns
// left, center or right side activation
IF column = 8 OR column = 7 OR column = 6 THEN
right_side_active := 1;
END_IF;
IF column = 6 OR column = 5 OR column = 4 OR column = 3 THEN
center_horizontal_active := 1;
END_IF;
IF column = 3 OR column = 2 OR column = 1 THEN
left_side_active := 1;
END_IF;
// top, center or bottom side activation
IF row = 1 OR row = 2 OR row = 3 THEN
top_active := 1;
END_IF;
IF row = 3 OR row = 4 OR row = 5 OR row = 6 THEN
center_vertical_active := 1;
END_IF;
IF row = 6 OR row = 7 OR row = 8 THEN
bottom_active := 1;
END_IF;
END_IF;
END_FOR;
END_FUNCTION_BLOCK
The map is divided into 3 horizontal and 3 vertical zones, with the middle zones extending into the extreme zones. Such a division may be suitable for various purposes, or may be replaced by a completely different approach, such as defining your own zone in the form of a square or rectangle as shown in the Sensor Use example.
1.1.1 VAR_INPUT
Variable |
Type |
Description |
sensor_data |
TCIB_GRIDEYE_DATA |
Reference to the entire sensor structure |
temperature_offset |
REAL |
Offset of measured temperatures, which is added to the values from the sensors |
temperature_trigger |
REAL |
The temperature above which the presence of an object is indicated |
1.1.2 VAR_OUTPUT
Variable |
Type |
Description |
temperature_difference_matrix |
ARRAY [1..64] of REAL |
Field of values of differences of individual sensors in the map against the reference temperature |
temperature_matrix |
ARRAY [1..64] of REAL |
Field of measured absolute temperatures of individual sensors (reference + measured value + offset) |
presence_matrix |
ARRAY [1..64] of BOOL |
Field indicating which sensors measured the temperature exceeding the defined temperature temperature_trigger |
left_side_active |
BOOL |
Indication of the active left zone in the temperature map |
center_horizontal_active |
BOOL |
Indication of the active middle horizontal zone in the temperature map |
right_side_active |
BOOL |
Indication of the active right zone in the temperature map |
top_active |
BOOL |
Indication of the active upper zone in the temperature map |
center_vertical_active |
BOOL |
Indication of the active middle vertical zone in the temperature map |
bottom_active |
BOOL |
Indication of the active lower zone in the temperature map |
1.2 Averaging
The function returns the average, which is always updated with the newly measured value (in the example, when calling the function from the main program prgMain, a timer is used, according to which the new value is measured every 10 s and not according to the frequency of reading data from the sensor). For this method of averaging, it is not necessary to remember the field of previous values, but only the number of values from which it is averaged. As the amount of data increases, the variable n becomes high, which means that sudden changes in the measured data will have an ever smaller effect the longer the averaging takes place. However, this can also be a disadvantage, and there are a number of solutions and approaches to the issue of averaging in general, depending on the type of application. For example, an hourly or daily average could be calculated for values measured every 10 s, etc.
FUNCTION fAverage : REAL
VAR_INPUT
mean : REAL;
NewValue : REAL;
n : UDINT;
END_VAR
VAR_IN_OUT
END_VAR
VAR
ignoreData : BOOL := FALSE;
END_VAR
VAR_TEMP
END_VAR
IF System_S.S2_3 OR System_S.S2_4 THEN
ignoreData := TRUE;
mean := NewValue;
END_IF;
IF NOT ignoreData THEN
mean := mean + (NewValue - mean)/UDINT_TO_REAL(n);
END_IF;
ignoreData := FALSE;
fAverage := mean;
END_FUNCTION
1.3 People counter
The counting of people depends on the orientation of the sensor. The example defines from which side the people who entered the space are counted, and vice versa, therefore, the people who came out of the space are counted. The variables are named specifically for the left side, the horizontal center, and the right side, but the same principle would be used when counting in a vertical orientation.
FUNCTION_BLOCK fbPeopleCounter
VAR_INPUT
left_to_right : BOOL := 0;
right_to_left : BOOL := 1;
right_side_active : BOOL := 0;
center_horizontal_active : BOOL := 0;
left_side_active : BOOL := 0;
reset : BOOL R_EDGE;
END_VAR
VAR_OUTPUT
people_counter : INT := 0;
people_in : INT := 0;
people_out : INT := 0;
END_VAR
VAR_IN_OUT
END_VAR
VAR
right_side_r_edge : R_TRIG;
right_side_f_edge : F_TRIG;
left_side_r_edge : R_TRIG;
left_side_f_edge : F_TRIG;
entrance_initiated : BOOL := 0;
exit_initiated : BOOL := 0;
END_VAR
VAR_TEMP
END_VAR
// Detection of edges for initiation of entrance and exit
right_side_r_edge(CLK := right_side_active);
right_side_f_edge(CLK := right_side_active);
left_side_r_edge(CLK := left_side_active);
left_side_f_edge(CLK := left_side_active);
// From right to left
IF right_to_left AND NOT left_to_right THEN
// Entrance
IF right_side_r_edge.Q AND NOT exit_initiated THEN
entrance_initiated := TRUE;
END_IF;
IF entrance_initiated AND left_side_f_edge.Q AND NOT (center_horizontal_active OR right_side_active)THEN
people_counter := people_counter + 1;
people_in := people_in + 1;
END_IF;
// Exit
IF left_side_r_edge.Q AND NOT entrance_initiated THEN
exit_initiated := TRUE;
END_IF;
IF exit_initiated AND right_side_f_edge.Q AND NOT (center_horizontal_active OR left_side_active) THEN
people_counter := people_counter - 1;
people_out := people_out + 1;
END_IF;
IF NOT (right_side_active OR center_horizontal_active OR left_side_active) THEN
entrance_initiated := FALSE;
exit_initiated := FALSE;
END_IF;
END_IF;
// From left to right
IF left_to_right AND NOT right_to_left THEN
// Entrance
IF left_side_r_edge.Q AND NOT exit_initiated THEN
entrance_initiated := TRUE;
END_IF;
IF entrance_initiated AND right_side_f_edge.Q AND NOT (center_horizontal_active OR left_side_active)THEN
people_counter := people_counter + 1;
people_in := people_in + 1;
END_IF;
// Exit
IF right_side_r_edge.Q AND NOT entrance_initiated THEN
exit_initiated := TRUE;
END_IF;
IF exit_initiated AND left_side_f_edge.Q AND NOT (center_horizontal_active OR right_side_active) THEN
people_counter := people_counter - 1;
people_out := people_out + 1;
END_IF;
IF NOT (right_side_active OR center_horizontal_active OR left_side_active) THEN
entrance_initiated := FALSE;
exit_initiated := FALSE;
END_IF;
END_IF;
// Reset
IF reset THEN
people_counter := 0;
people_in := 0;
people_out := 0;
END_IF;
END_FUNCTION_BLOCK
1.3.1 VAR_INPUT
Variable |
Type |
Description |
left_to_right |
BOOL |
Count from left to right - the entrance is on the left |
right_to_left |
BOOL |
Count from right to left - the entrance is on the right |
right_side_active |
BOOL |
Active right part of the sensor map - information from the function block for data processing from the sensor |
center_horizontal_active |
BOOL |
Active middle part of the sensor map - information from the function block for data processing from the sensor |
left_side_active |
BOOL |
Active left part of the sensor map - information from the function block for data processing from the sensor |
reset |
BOOL_R_EDGE |
The calculated values are reset to the leading edge of the reset signal |
1.3.1 VAR_OUTPUT
Variable |
Type |
Description |
people_counter |
INT |
The number of people currently inside |
people_in |
INT |
The total number of people that entered the space during the measurement |
people_out |
INT |
The total number of people who left the room during the measurement |