Hello, I am having trouble getting my GM4108 motor working with my AS5047 encoder. I have it set up with an on-axis magnet. The strange thing is in SimpleFOCStudio the encoder feedback looks accurate however the motor does very erratic behavior. The motor works well in open-loop velocity and angle. I believe I have the pole pairs correct because when I move the angle by 6.28 it does a perfect 360 degree rotation (at least perfect by eyesight). When I run in closed loop velocity mode, the motor most of the time will jerk and not move at all. When I run in torque mode the motor does not spin but it does have resistance while I am trying to move it. The resistance increases as I increase the torque mode target value. I had another set up using the same board with a GM2804 motor with the encoder in the same setup and that one I was able to get working perfectly in closed-loop mode. I have even tried moving the ports I am plugging into on my PCB to see if it was a hardware issue with my second motor driver on my custom PCB. I have been looking at forums for hours today so any help would be greatly appreciated. Thanks!
Hardware Specs:
GM4108 Hollow Shaft Gimbal Motor
Custom AS5047 Encoder Board (communicates over SPI)
DRV8313 Driver controlled by a custom RP2350 board
Code:
#include <SimpleFOC.h>
#include <SPI.h>
#include <SimpleFOCDrivers.h>
#include "encoders/as5047/MagneticSensorAS5047.h"
#include "encoders/as5047/AS5047.h"
#include "encoders/as5048a/MagneticSensorAS5048A.h"
#include <Adafruit_NeoPixel.h>
// pin definitions
#define D1_IN1 34
#define D1_IN2 35
#define D1_IN3 36
#define D1_EN1 37
#define D1_EN2 38
#define D1_EN3 39
#define D1_nRESET 33
#define D1_nSLEEP 32
#define D1_nCOMPO 31
#define D1_nFAULT 30
#define D2_IN1 6
#define D2_IN2 7
#define D2_IN3 8
#define D2_EN1 10
#define D2_EN2 11
#define D2_EN3 16
#define D2_nRESET 5
#define D2_nSLEEP 4
#define D2_nCOMPO 3
#define D2_nFAULT 2
#define ENC1_CSn 13
#define ENC2_CSn 29
#define MISO 12
#define MOSI 15
#define CLK 14
#define MON_VIN 47
#define MON_5V 46
#define WS0_DIN 27
#define NUM_LEDS 2
// motor instance - driver 1
BLDCMotor yaw_motor = BLDCMotor(11, 4.7, 25);
//motor instance - driver 2
BLDCMotor pitch_motor = BLDCMotor(7, 9, 100);
// driver 1 instance
BLDCDriver3PWM yaw_driver = BLDCDriver3PWM(D1_IN1, D1_IN2, D1_IN3, D1_EN1, D1_EN2, D1_EN3);
// driver 2 instance
BLDCDriver3PWM pitch_driver = BLDCDriver3PWM(D2_IN1, D2_IN2, D2_IN3, D2_EN1, D2_EN2, D2_EN3);
SPISettings settings = SPISettings(1'000'000, AS5047_BITORDER, SPI_MODE1);
MagneticSensorAS5047 yaw_encoder = MagneticSensorAS5047(ENC1_CSn, false, settings);
MagneticSensorAS5047 pitch_encoder = MagneticSensorAS5047(ENC2_CSn, false, settings);
// WS2812b LEDs
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, WS0_DIN, NEO_GRB + NEO_KHZ800);
// commander interface
Commander command = Commander(Serial);
void onYawMotor(char* cmd){ command.motor(&yaw_motor, cmd); }
void onPitchMotor(char* cmd){ command.motor(&pitch_motor, cmd); }
void setup() {
// use monitoring with serial for motor init
// monitoring port
Serial.begin(115200);
SPI1.setTX(MOSI);
SPI1.setRX(MISO);
SPI1.setSCK(CLK);
// SPI1.begin();
// active-low reset pin
pinMode(D1_nRESET, OUTPUT);
pinMode(D2_nRESET, OUTPUT);
digitalWrite(D1_nRESET, HIGH);
digitalWrite(D2_nRESET, HIGH);
//sleep pin high to enable
pinMode(D1_nSLEEP, OUTPUT);
pinMode(D2_nSLEEP, OUTPUT);
digitalWrite(D1_nSLEEP, HIGH);
digitalWrite(D2_nSLEEP, HIGH);
yaw_encoder.init(&SPI1);
pitch_encoder.init(&SPI1);
// configure driver
yaw_driver.voltage_power_supply = 20;
yaw_driver.init();
pitch_driver.voltage_power_supply = 20;
pitch_driver.init();
pitch_motor.linkSensor(&pitch_encoder);
yaw_motor.linkSensor(&yaw_encoder);
pitch_motor.linkDriver(&pitch_driver);
yaw_motor.linkDriver(&yaw_driver);
// initialize sensor sensor hardware
// choose FOC modulation
yaw_motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
pitch_motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
// set control loop type to be used
yaw_motor.controller = MotionControlType::angle;
pitch_motor.controller = MotionControlType::angle;
// yaw_motor initialization values
// yaw_motor.zero_electric_angle = 0.22;
// yaw_motor.sensor_direction = Direction::CW;
yaw_motor.LPF_velocity.Tf = 0.05;
yaw_motor.voltage_sensor_align = 8;
//limits
yaw_motor.voltage_limit = 6;
yaw_motor.velocity_limit = 10;
yaw_motor.current_limit = 1;
//pid tuning
yaw_motor.PID_velocity.P = 0;
yaw_motor.PID_velocity.I = 0;
yaw_motor.PID_velocity.D = 0;
yaw_motor.PID_velocity.limit = yaw_motor.voltage_limit;
yaw_motor.P_angle.P = 0;
//monitoring
yaw_motor.useMonitoring(Serial);
yaw_motor.monitor_downsample = 1000;
// pitch_motor initialization values
//pitch_motor.zero_electric_angle = 3.05;
// pitch_motor.sensor_direction = Direction::CW;
pitch_motor.LPF_velocity.Tf = 0.05;
pitch_motor.LPF_angle = 0.05;
pitch_motor.voltage_sensor_align = 6;
//limits
pitch_motor.voltage_limit = 6;
pitch_motor.velocity_limit = 40;
pitch_motor.current_limit = 0.5;
//pid tuning
pitch_motor.PID_velocity.P = 0.05;
pitch_motor.PID_velocity.I = 0;
pitch_motor.PID_velocity.D = 0;
pitch_motor.PID_velocity.limit = pitch_motor.voltage_limit;
pitch_motor.PID_velocity.output_ramp = 1000.0;
pitch_motor.P_angle.P = 12;
pitch_motor.P_angle.output_ramp = 100.0;
pitch_motor.P_angle.limit = 40.0;
//monitoring
pitch_motor.useMonitoring(Serial);
pitch_motor.monitor_downsample = 1000;
//initialization sequence
yaw_motor.init();
pitch_motor.init();
// align sensor and start FOC
Serial.println("Initialize Yaw");
yaw_motor.initFOC();
Serial.println("Initialize Pitch");
pitch_motor.initFOC();
//disable motors initially
yaw_motor.disable();
pitch_motor.disable();
// set the initial target value
yaw_motor.target = 0;
pitch_motor.target = 0;
// define the motor id
command.add('Y', onYawMotor, "yaw_motor");
command.add('P', onPitchMotor, "pitch_motor");
_delay(1000);
//WS2812b leds
strip.begin();
strip.setBrightness(15);
strip.show();
}
void loop() {
// iterative setting FOC phase voltage
yaw_encoder.update();
pitch_encoder.update();
AS5047Diagnostics pitch_diagnostics = pitch_encoder.readDiagnostics();
AS5047Diagnostics yaw_diagnostics = yaw_encoder.readDiagnostics();
yaw_motor.loopFOC();
pitch_motor.loopFOC();
// diagnostics print to serial monitor
// float yaw_pos = yaw_encoder.getAngle();
// float pitch_pos = pitch_encoder.getAngle();
// float yaw_angle = yaw_motor.shaft_angle;
// float pitch_angle = pitch_motor.shaft_angle;
// Serial.print(yaw_pos);
// Serial.print(" : ");
// Serial.print(yaw_angle);
// Serial.print(" : ");
// Serial.print(yaw_motor.target);
// Serial.print(" : ");
// Serial.println(pitch_pos);
// Serial.print(" : ");
// Serial.print(pitch_angle);
// Serial.print(" : ");
// Serial.println(pitch_motor.target);
strip.clear();
if ((pitch_diagnostics.magh || pitch_diagnostics.magl || pitch_encoder.isErrorFlag()) == 1) {
if (pitch_encoder.isErrorFlag() == 1){
strip.setPixelColor(1, strip.Color(150,0,0));
} else if (pitch_diagnostics.magh == 1){
strip.setPixelColor(1, strip.Color(200,100,0));
} else if (pitch_diagnostics.magl == 1){
strip.setPixelColor(1, strip.Color(0,0,150));
}
} else {
strip.setPixelColor(1, strip.Color(0,150,0));
}
if ((yaw_diagnostics.magh || yaw_diagnostics.magl || yaw_encoder.isErrorFlag()) == 1) {
if (yaw_encoder.isErrorFlag() == 1){
strip.setPixelColor(0, strip.Color(150,0,0));
} else if (yaw_diagnostics.magh == 1){
strip.setPixelColor(0, strip.Color(200,100,0));
} else if (yaw_diagnostics.magl == 1){
strip.setPixelColor(0, strip.Color(0,0,150));
}
} else {
strip.setPixelColor(0, strip.Color(0,150,0));
}
strip.show();
// iterative function setting the outer loop target
// velocity, position or voltage
// if target not set in parameter uses motor.target variable
yaw_motor.move();
pitch_motor.move();
yaw_motor.monitor();
pitch_motor.monitor();
// user communication
command.run();
}
Final note:
An odd thing I have noticed is that the initialization never gets the pole pairs correct and the zero electrical angle is wildly different each time. Based on the behavior I believe it is a configuration issue not a hardware issue (since open-loop works so well). I also don’t believe it is an encoder issue because the angle in SimpleFOCStudio looks great. I am at a loss because it was a lot easier to get the GM2804 motor working with the exact same setup. Any help would be appreciated!