Arduino MATLAB sin function loss of resolution with data
3 次查看(过去 30 天)
显示 更早的评论
Hellow everyone.This is my first time dule with Adriuno Genuino UNO communicate with matlab.To communicate with two float ,I try use two sine and "," ,brutal way ,to let matlab calculate .When there is communicate with one sine data,sometimes it communicate with low resonance data .Seem's there is data cover each other during communicate.Situation also happen on using two data communication .I wondering why this problem happen .
This is my Ardiuno code:
int i = 0;
// The setup routine runs once when you press reset:
void setup() {
// Initialize serial communication at 9600 bits per second:
Serial.begin(115200);
}
// The loop routine runs over and over again forever:
void loop() {
Serial.print(sin(50*i/360));
Serial.print(",");
Serial.print(sin(20*i/360));
Serial.write(13);
Serial.write(10);
i += 1;
}
And my matlab code:
clc
close all
clear
s = serialport("COM3", 115200);
% communicate with Arduino COM & SERIAL_SPEED from Arduino
configureTerminator(s,"CR/LF");
s.UserData = struct("Data",[],"Count",1);
configureCallback(s,"terminator",@readSineWaveData);
function readSineWaveData(src, ~)
% Read the ASCII data from the serialport object.
data = readline(src);
try
%wtih two data
SIndex = str2double(strsplit(data, ','));
a = SIndex(1);
b = SIndex(2);
src.UserData.Data(end+1,1) = a;
src.UserData.Data(end,2)= b;
catch
%wtih one data
a = str2double(data);
src.UserData.Data(end+1) = a;
end
% Update the Count value of the serialport object.
src.UserData.Count = src.UserData.Count + 1;
if length(src.UserData.Data) <= 150
plot(src.UserData.Data);
else
plot(length(src.UserData.Data)-150:length(src.UserData.Data), src.UserData.Data(end-150:end));
pause(0.01);
end
% If 1001 data points have been collected from the Arduino, switch off the
% callbacks and plot the data.
if src.UserData.Count > 100
configureCallback(src, "off");
% plot(src.UserData.Data(2:end));
end
end
communicate with two sine & ","
communicate only sine .
Thank you
1 个评论
Walter Roberson
2024-2-22
Serial.print(sin(50*i/360));
The angle argument is in radians.
I suspect you are encountering aliasing.
回答(2 个)
Hassaan
2024-2-22
编辑:Hassaan
2024-2-22
Serial Buffer Overflow: At high data rates, the serial buffer on the Arduino or in MATLAB might be overflowing. Make sure that MATLAB reads and clears the buffer fast enough to keep up with the incoming data rate. If the data rate is too high, consider lowering the frequency at which data is sent or increasing the baud rate, although you've already set it to a high value (115200).
Data Parsing Errors: Your MATLAB code assumes that each line read from the serial buffer is a complete, correctly formatted message. If messages are being split across buffer reads, your code might be trying to parse incomplete data. You need to ensure that each readline command in MATLAB reads a full line of data. You can add a delimiter or terminator in your Arduino code to mark the end of a data packet.
Signal Conditioning: Ensure that the analog signals you're trying to measure are properly conditioned before they're sampled. Noise, interference, or inadequate sampling rates can corrupt your data. Aliasing could also play a role.
Integer Division in Arduino: In your Arduino code, 50*i/360 will perform an integer division when i is less than 360, resulting in 0. You need to ensure that you're performing floating-point division by using 50.0*i/360.0.
Radian/Degree: Verify the argument type for sin(). Default is radian input.
Serial.print(sin(50*i*PI/180)); // Convert degrees to radians
Data Type Conversion: Make sure that the data sent from Arduino can be correctly interpreted as floating-point numbers in MATLAB. Any noise or corruption can make the conversion fail, resulting in outliers or NaN values.
Arduino Code
- Ensure that floating-point division is used.
- Add a delay in the loop to slow down the data rate if necessary.
- Use a more robust data packet structure with start and end markers.
int i = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
//float sine1 = sin(2 * PI * 50.0 * i / 360.0);
//float sine2 = sin(2 * PI * 20.0 * i / 360.0);
float sine1 = sin(50*i*PI/180); // or sin(50.0*i*PI/180.0)
float sine2 = sin(20*i*PI/180); // or sin(20.0*i*PI/180.0)
Serial.print(sine1, 4); // Print with 4 decimal places
Serial.print(",");
Serial.print(sine2, 4); // Print with 4 decimal places
Serial.println(); // println adds both CR and LF
i += 1;
// Note: Add a delay to control data rate. May need adjustment as per your need. Experiment with it
delay(10);
// The delay(10) function introduces a delay of 10 milliseconds between each data point.
//You might need to adjust this value depending on your desired output rate and processing requirements.
}
MATLAB Code
- Make sure that the plot is not slowing down the data reading.
- Use a fixed-size buffer to store data to avoid growing the array dynamically, which can be slow.
% Approach 1
function readSineWaveData(src, ~)
% Read the ASCII data from the serialport object.
data = readline(src);
% Parse the comma-separated sine values
sineValues = str2double(split(data, ','));
if length(sineValues) == 2
src.UserData.Data(src.UserData.Count,:) = sineValues';
src.UserData.Count = src.UserData.Count + 1;
end
% Plot the data
if mod(src.UserData.Count, 150) == 0
plot(src.UserData.Data(:,1), 'r');
hold on;
plot(src.UserData.Data(:,2), 'b');
hold off;
drawnow;
end
% Stop the callback if enough data has been collected
if src.UserData.Count > 1000
configureCallback(src, "off");
disp('Data collection stopped.');
end
end
Please adjust the size of UserData.Data to accommodate the expected number of data points and preallocate it in your main MATLAB script. Also, ensure that UserData.Count is initialized to 1.
- Make sure to flush the input buffer before starting to read data to discard any incomplete or corrupted data from previous reads.
- Include more robust error handling and parsing to ensure that you always have two floating-point numbers separated by a comma.
- If there's a possibility of data overlap or missed readings, increase the robustness of your data parsing logic to handle these cases.
% Approach 2
function readSineWaveData(src, ~)
% Read the ASCII data from the serialport object.
data = readline(src);
% Data parsing
splitData = strsplit(data, ',');
if numel(splitData) == 2
a = str2double(splitData{1});
b = str2double(splitData{2});
if ~isnan(a) && ~isnan(b)
src.UserData.Data(end+1,:) = [a, b];
end
end
% Update plot
if length(src.UserData.Data) <= 150
plot(src.UserData.Data);
else
plot(length(src.UserData.Data)-150:length(src.UserData.Data), src.UserData.Data(end-150:end, :));
pause(0.01);
end
% If enough data points have been collected, switch off the callbacks.
if size(src.UserData.Data, 1) > 1000
configureCallback(src, "off");
end
end
Note:
- Try the above code.
- I haven't test it on my setup. I tried to setup the code as I mainly do for my client project. It may need adjustments as per your need.
-----------------------------------------------------------------------------------------------------------------------------------------------------
If you find the solution helpful and it resolves your issue, it would be greatly appreciated if you could accept the answer. Also, leaving an upvote and a comment are also wonderful ways to provide feedback.
It's important to note that the advice and code are based on limited information and meant for educational purposes. Users should verify and adapt the code to their specific needs, ensuring compatibility and adherence to ethical standards.
Professional Interests
- Technical Services and Consulting
- Embedded Systems | Firmware Developement | Simulations
- Electrical and Electronics Engineering
Feel free to contact me.
0 个评论
Alexander
2024-2-22
You should define a sample rate in your program, e.g.:
const unsigned long AbtastRate = 60000; // 60000 entspricht einer Minute
To get a constant sampling insert something like the following snippet at the end of your "void loop()":
// delay in milliseconds before the next reading. -1 to adjust.
while (currentMillis - previousMillis <= AbtastRate - 1) {
currentMillis = millis();
}
//Serial.println(currentMillis - previousMillis); // For debugging purpose only.
previousMillis = currentMillis;
Instead you can use "delay()" also, but in this case the sampling rate is not as precise.
1 个评论
Alexander
2024-2-22
Just forgotten:
unsigned long currentMillis = millis();
sould be at the beginning of "loop()".
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 MATLAB Support Package for Arduino Hardware 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!