How to communicate with two Raspberri Pis from MATLAB in parallel

4 visualizaciones (últimos 30 días)
Robert Morawski
Robert Morawski el 21 de Mzo. de 2024
Editada: Robert Morawski el 26 de Mzo. de 2024
Using Matlab on a PC, I am trying to run two functions in parallel on two raspberry pis as shown in the example code below:
clear all;
r1 = raspi('192.168.5.120', 'pi', 'raspberry'); %tx
r2 = raspi('192.168.7.201', 'pi', 'raspberry'); %rx
delete(gcp('nocreate'));
parpool(2); %start parallel pool
parfor t=1:2
if t==1
system(r1,'python update_att.py');
disp("par1");
end
if t==2
system(r2,'python update_att.py');
disp("par2");
end
end
delete(gcp('nocreate'));
The script run through the system() function does not matter and the addresses are local. I appear to be running into a problem where each of the workers in parfor is trying to reinitialize the rasperry pi initializations defined at the start of the code as shown below:
This end up resulting in the error "The source code (path to script) for the parfor-loop that is trying to execute on the worker could not be found." caused by "Undefined function 'execute' for input arguments of type 'double'."
When I modify the code such that the raspberry pi connections are initialized within the parfor loop, the code executed properly (as shown below). However, it is necessary that I initialize my raspberry pi connections outside the parfor loop as the parfor loop itself is present in a for loop in my actual code and I desire an optimal execution time (that does not require the lengthy connection to be established each iteration of the parfor loop)
clear all;
delete(gcp('nocreate'));
parpool(2); %start parallel pool
parfor t=1:2
if t==1
r1 = raspi('192.168.5.120', 'pi', 'raspberry'); %tx
system(r1,'python update_att.py');
disp("par1");
end
if t==2
r2 = raspi('192.168.7.201', 'pi', 'raspberry');
system(r2,'python update_att.py');
disp("par2");
end
end
delete(gcp('nocreate'));
If anyone has encountered a similar problem or knows of a fix please let me know. Thanks!

Respuestas (2)

Raymond Norris
Raymond Norris el 21 de Mzo. de 2024
I would suggest creating the raspberry connections on the workers, before calling parfor, with parallel.pool.Constant
tx = parallel.pool.Constant( ...
@() raspi('192.168.5.120', 'pi', 'raspberry'));
rx = parallel.pool.Constant( ...
@() raspi('192.168.7.201', 'pi', 'raspberry'));
And then reference rx and tx as such
system(tx.Value,'python update_att.py');
Next, I would add a second function handle to the constant to close the connection on the workers (let's assume the raspi object has a close method to close the connection).
tx = parallel.pool.Constant( ...
@() raspi('192.168.5.120', 'pi', 'raspberry'), ...
@close);
rx = parallel.pool.Constant( ...
@() raspi('192.168.7.201', 'pi', 'raspberry'), ...
@close);
After the parfor, call
% Close Raspberry connection (i.e., close(rt.Value))
clear rx tx
Lastly, you might look at spmd instead of parfor. Conceptually, you're not running a for loop, you're running a client/server model (send/receive), but this might not fit for what you are doing.
clear all;
tx = parallel.pool.Constant( ...
@() raspi('192.168.5.120', 'pi', 'raspberry'), ...
@close);
rx = parallel.pool.Constant( ...
@() raspi('192.168.7.201', 'pi', 'raspberry'), ...
@close);
delete(gcp('nocreate'));
parpool(2); %start parallel pool
spmd
if spmdIndex==1
system(tx.Value,'python update_att.py');
disp("par1");
else
system(rx.Value,'python update_att.py');
disp("par2");
end
end
% Close Raspberry connection (i.e., close(rt.Value))
clear rx tx
delete(gcp('nocreate'));
  1 comentario
Robert Morawski
Robert Morawski el 22 de Mzo. de 2024
Editada: Robert Morawski el 26 de Mzo. de 2024
Thanks for the response, I have tried creating the Raspberry Pi connections as you suggested:
tx = parallel.pool.Constant( ...
@() raspi('192.168.5.120', 'pi', 'raspberry'));
rx = parallel.pool.Constant( ...
@() raspi('192.168.7.201', 'pi', 'raspberry'));
However, when doing so I encounter the error "Error using raspi.internal.RaspiBase (line 110) Cannot establish a connection to the board with device address "192.168.5.120". This error occurs before even reaching the parfor or spmd code (thus I believe it is an error in the initialization on the worker).
I thought maybe the workers did not have access to the pi, however when I conducted the following ping test I get the expected response:
clear all;
delete(gcp('nocreate'));
parpool(2);
commandToExecute = ['ping -n 1 ', '192.168.5.120'];
commandOutputs = cell(1, 1);
parfor i = 1:1
[~, commandOutput] = system(commandToExecute);
commandOutputs{i} = commandOutput;
end
commandOutput = commandOutputs{1};
if ~isempty(commandOutput)
disp(['Ping output: ', commandOutput]);
else
disp('Cmd failed');
end
delete(gcp('nocreate'));
Additionally, if I instead try to initialize the raspberry Pi connections as shown below I get the following error originating from the parfor loop/spmd block shown in earlier responses:
Error using remoteParallelFunction (line 94) Worker unable to find file. Error using parallel_function>make_general_channel/channel_general (line 852) Undefined function 'execute' for input arguments of type 'double'.
r1 = parallel.pool.Constant(...
raspi('192.168.5.120', 'pi', 'raspberry')); %tx
r2 = parallel.pool.Constant(...
raspi('192.168.7.201', 'pi', 'raspberry')); %rx
I also wanted to mention that the raspi() constructor and system function come from the MATLAB Support Package for Raspberry Pi Hardware. Please let me know if you know of any other potential fixes I can try. Thanks!
BTW, our project's goal is to execute the following repetetive 3 steps as fast as possible (<< 1 sec):
1) sending different very short data data files to multiple (8) RPis over Ethernet
2) all (8) RPi will act on sent data independently, and each will produce very short data result
3) the results from multiple RPis are collected, and new set of very short data data files is prepared
The above steps would be repeated in the loop until the optimum solution is found.
Can we expect any speed improvement for this application from parallel execution setup (parpool and spmd)? Any suggestions? TIA

Iniciar sesión para comentar.


Damian Pietrus
Damian Pietrus el 21 de Mzo. de 2024
Based on your code, it looks like you're using the Local/Processes cluster profile to run your parfor loop on. With this setup, each of the workers in the pool are their own separate process, with their own memory and Process ID which are separate from your client session. Therefore, in your first code block where you initialize the connection outside of the parfor loop, you have your client MATLAB session create a connection to the hardware. Then within the parfor loop an entirely different MATLAB process tries to use that connection, which is most likely why you get that warning. This would also be why initializing inside of the loop works, as the actual worker process establishes the connection instead of the client.
You could try to use a thread pool instead of a process pool, but I'm skeptical that the commands in your code will be supported. Give it a shot and let me know what happens:
clear all;
r1 = raspi('192.168.5.120', 'pi', 'raspberry'); %tx
r2 = raspi('192.168.7.201', 'pi', 'raspberry'); %rx
delete(gcp('nocreate'));
% Use a Thread Pool
parpool('Threads',2); %start parallel pool
parfor t=1:2
if t==1
system(r1,'python update_att.py');
disp("par1");
end
if t==2
system(r2,'python update_att.py');
disp("par2");
end
end
delete(gcp('nocreate'));
  1 comentario
Robert Morawski
Robert Morawski el 22 de Mzo. de 2024
Thanks for the response. When I try to initalize a thread pool instead I get the following error: "Use of function system is not supported on a thread-based worker." as you had predicted. I have also tried other fixes as detailed in my responses to some of the other answers to no avail.

Iniciar sesión para comentar.

Categorías

Más información sobre Parallel Computing Fundamentals en Help Center y File Exchange.

Productos


Versión

R2021b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by