How to communicate with two Raspberri Pis from MATLAB in parallel

22 views (last 30 days)
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!

Answers (2)

Raymond Norris
Raymond Norris on 21 Mar 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 Comment
Robert Morawski
Robert Morawski on 22 Mar 2024
Edited: Robert Morawski on 26 Mar 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

Sign in to comment.


Damian Pietrus
Damian Pietrus on 21 Mar 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 Comment
Robert Morawski
Robert Morawski on 22 Mar 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.

Sign in to comment.

Categories

Find more on MATLAB Support Package for Raspberry Pi Hardware in Help Center and File Exchange

Products


Release

R2021b

Community Treasure Hunt

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

Start Hunting!