Hello! I am controlling a brushless motor and trying to send the control signals from PC (Python) to Arduino via serial communication. The Arduino will send back the IMU reading. However, I'm facing a problem: the moment I run the Python code, the motor goes out of control (max. voltage).
- Where is the problem exactly?
- Are there possible delays in the communication? How to fix them?
Any kind of help will be very much appreciated. Attached codes below:
Arduino code:
////interrupt////////////////////////////////////////////////////////////////////////////////////////////
#include <avr/interrupt.h>
const uint8_t SOFT_SERIAL_INT = INT0; // use INT0 as the interrupt source
// MPU6050 data///////////////////////////////////////////////////////////////////////////////////////////
//MPU 6050 Libraries
#include <Wire.h>
#include <I2Cdev.h>
#include <MPU6050.h>
MPU6050 mpu;
// Raw data
int16_t ax, ay, az;
int16_t gx, gy, gz;
// Converted data
float axConv;
float ayConv;
float azConv;
float gyConv;
// Computations
float thetax_rad;
float thetaz_rad;
float go;
float theta_avg_rad;
float theta_avg_deg;
/**** difference between sensed and actual angles *****////////////////////////////////////////////////////
float offset= 0.01;
//Variables for the low pass filter
float F_theta_old=0;
float F_theta_new=0;
float F_theta_deg;
int F_theta_new_deg;
/**** Conversion *****//////////////////////////////////////////////////////////////////////////////////////
float Deg_to_Rad = 3.141592654/180;
float Rad_to_Deg = 180/3.141592654;
//Brushless motor library
#include<Servo.h>
#define ESC_PIN 2 //PWM Connected to PIN 2
Servo esc;
int theta;
/////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(2), serialEvent, CHANGE);
//MPU 5060 Setup
Wire.begin();
mpu.initialize();
esc.attach(ESC_PIN, 1000, 2000); //calibrating the motor for min and max rpm esc.write(180);
delay(5000);
esc.write(0);
delay(2000);
esc.write(10); esc.writeMicroseconds(1000); //min PWM
delay(5000); //give the setup some delay
}
void loop()
{
//nothing, just waits for an interrupt command :)
ReadIMU();
delay(50);
}
void serialEvent() //to read any command
{
EIFR |= (1 << INTF0); // set the interrupt flag
if (Serial.available()>=2)
{
byte bytes[2];
Serial.readBytes(bytes, 2);
Serial.read(); // discard newline character
int16_t command = bytes[0];
int16_t pwm = bytes[1];
/* for debugging
byte packet[2];
packet[0] = command;
packet[1] = value;
Serial.write(packet, sizeof(packet));
*/
if (command == 1) //send IMU values
{
ReadIMU();
Serial.write(F_theta_new_deg);
}
else if (command == 2)
{
WriteToMotor(pwm);
Serial.write(F_theta_new_deg);
}
}
}
void ReadIMU()
{
// MPU readings, mapping, and related computations
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
axConv = ax/16384.0;
ayConv = ay/16384.0;
azConv = az/16384.0;
gyConv = gy/131.0;
go = sqrt(pow(axConv,2) + pow(ayConv,2) + pow(azConv,2));
thetax_rad = acos(axConv/go);
thetaz_rad = asin(azConv/go);
theta_avg_rad = (thetaz_rad+thetax_rad)/2 + offset;
//theta_avg_deg = theta_avg_rad*Rad_to_Deg;
// Low pass filter
F_theta_new= (0.7)*F_theta_old +(0.3)*theta_avg_rad;
F_theta_new_deg= F_theta_new*Rad_to_Deg;
F_theta_old = F_theta_new;
}
void WriteToMotor(int pwm) //pwm is sent as an argument
{
int mappedPWM = map(pwm, 0, 255, 1000, 1450);
esc.writeMicroseconds(mappedPWM);
}
Python code:
import serial
import threading
import time
import struct
import csv
import math
import time
import matplotlib.pyplot as plt
# Establish connection with Arduino
ser = serial.Serial('/dev/tty.usbmodem14201', 115200)
# Check if the connection is open
if ser.isOpen():
print("Connection established!")
else:
print("Connection failed!")
# Fixed step
step_delay = 100 # millis
# For all cases,
# Number of steps required = interval / step_delay
# For 10 seconds period
interval_10 = 10000 # 10 seconds
steps_10 = interval_10 // step_delay
# For 5 seconds interval
interval_5 = 5000
steps_5 = interval_5 // step_delay
# For 5 seconds interval
interval_15 = 15000
steps_15 = interval_15 // step_delay
pwm = 0
pwm_values = []
def serialCommunication(newPWM, oldPWM=None):
if newPWM != oldPWM:
oldPWM = newPWM
# send PWM & read Theta
mapped_PWM = int((newPWM - 1000)*255/450)
ser.write(struct.pack('BB', 2, mapped_PWM))
else:
# just read Theta
ser.write(struct.pack('BB', 1, 0))
# Wait for the message to be sent
time.sleep(0.05)
# Read from the serial port
data = ser.read()
# Print the received data
decimal_val = ord(data)
print("PWM signal sent: " + str(mapped_PWM) + " " + str(int(newPWM)))
print("Theta value received: " + str(decimal_val))
# Open the file and write data to it
with open('profile2data.csv', mode='a', newline='') as file:
writer = csv.writer(file)
writer.writerow([newPWM, decimal_val])
def Pwm_signal():
global pwm, pwm_values
with open('profile2data.csv', mode='w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['PWM', 'Theta'])
# Sin wave, for 15 seconds and frequency= 0.25 Hz, fluctuating between 1200 and 1300
freq = 0.25 # Hz
period = 1.0 / freq # seconds
amplitude = (1300 - 1200) // 2
offset = 1200 + amplitude
for i in range(steps_15 + 1):
rad = 2 * math.pi * freq * i * step_delay / 1000.0
pwm = int(amplitude * math.sin(rad)) + offset
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Step to 1400
pwm = 1400
for i in range(steps_5+1):
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Step to 1300
pwm = 1300
for i in range(steps_5+1):
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Step to 1200
pwm = 1200
for i in range(steps_5+1):
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Linear ramp from 1200 to 1300 for 5 seconds
for i in range(steps_5 + 1):
pwm = int((i / steps_5) * (1300 - 1200) + 1200)
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Linear ramp from 1300 to 1200 for 5 seconds
for i in range(steps_5, -1, -1):
pwm = int((i / steps_5) * (1300 - 1200) + 1200)
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Linear ramp from 1200 to 1400 for 5 seconds
for i in range(steps_5 + 1):
pwm = int((i / steps_5) * (1400 - 1200) + 1200)
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Ramp from 1400 to 1050 for 10 seconds
for i in range(steps_10, -1, -1):
pwm = int((i / steps_10) * (1050 - 1400) + 1400)
serialCommunication(pwm)
time.sleep(step_delay / 1000.0)
# Set the motor to its minimum value
pwm = 1050
serialCommunication(pwm)
# Add a delay to allow the Arduino to reset
time.sleep(12)
# create two threads
thread_1 = threading.Thread(target=Pwm_signal)
# start the threads
thread_1.start()
# wait for the threads to finish
thread_1.join()