SimpleFOC on VESC 6 (Flipsky FSVESC 6.7)

Evening,

I’m testing the SimpleFOC library on a VESC 6 with some confident results! i started from this topic and simply re-mapped the MCU pins following the schematic of the new VESC version. I’m using PlatformIO and had a bit of trouble getting the serial to work via USB (had to put monitor_dtr = 1 on the .ino) but couldn’t get USART6 functioning (for now i’ll not use it).

I had already done some testing on the B-G431B-ESC1 but the initFOC() couldn’t calculate the number of motor poles and the zero electric angle correctly probably due to a faulty motor.

I’m now using a Flipsky 6354 and the init routine completes successfully.
However the strange fact remains that the motor moves smoothly in the init routine but slightly jerky in normal operation (“sounds” like a stepper motor) like the previous motor plus B-G431B-ESC1, so I assume it is related to some parameter or due to the FOC algorithm.

In any case, even though I haven’t fine tuned the PID’s both speed and position control works.

At the moment the main problem is that, using the commander, the motor in speed control reaches maximum 80 rad/s, which is very strange. Does anyone have any advice for me?

The MCU should be set with all the peripherals set to the maximum clock speed, I don’t think it’s a problem of the code execution, but tomorrow for sure I will check the speed of the void loop()…

I tried initializing the current sensing on the three phases as well, but at the moment although currentSense.driverAlign() seems to work fine, the current control doesn’t work. I’ll fix it after have a good ctr performances with the voltage mode.

Just to mention the application, the idea is to use this driver with SimpleFOC to implement a joint control of a lower limb exoskeleton (open-source for research purposes)

Thank you for the help!

#include <Arduino.h>
#include <board_vesc_6.h>
#include <SimpleFOC.h>

const int motorPolePairs = 7;

BLDCMotor motor = BLDCMotor(motorPolePairs);
BLDCDriver6PWM driver(H1, L1, H2, L2, H3, L3, EN_GATE);
//InlineCurrentSense currentSense = InlineCurrentSense(0.0005, 20, CURRENT_1, CURRENT_2, CURRENT_3);

// encoder instance

HallSensor sensor = HallSensor(HALL_1, HALL_2, HALL_3, motorPolePairs);

// Interrupt routine intialisation
// channel A and B callbacks
void doA() {sensor.handleA();}
void doB() {sensor.handleB();}
void doC() {sensor.handleC();}

float target = 0;                       // angle set point variable
Commander command = Commander(Serial);        // instantiate the commander


void onMotor(char* cmd) {
  command.motor(&motor, cmd);
}
void doTarget(char* cmd) {
  command.scalar(&target, cmd);
}

void setup() {

  sensor.init();                              // initialize sensor hardware
  sensor.enableInterrupts(doA, doB, doC);

  driver.voltage_power_supply = 24;           // power supply voltage [V]

  driver.init();

  motor.phase_resistance = 0.053; // [Ohm]
  //motor.voltage_limit = 0.5; // [V] - if phase
  motor.current_limit = 65;      // [Amps] - if phase resistance defined
  motor.velocity_limit = 200; // [rad/s] 5 rad/s cca 50rpm

  // link the motor to the sensor
  motor.linkSensor(&sensor);
  // link the motor and the driver
  motor.linkDriver(&driver);

  
  // aligning voltage [V]
  motor.voltage_sensor_align = 1.5;
  // index search velocity [rad/s]
  motor.velocity_index_search = 10;

  //currentSense.init();      // current sensing
  //currentSense.driverAlign(&driver, motor.voltage_sensor_align);
  //currentSense.skip_align = true;
  //motor.linkCurrentSense(&currentSense);

  // set motion control loop to be used
  motor.controller = MotionControlType::velocity;
  //motor.torque_controller = TorqueControlType::foc_current;
  //motor.foc_modulation = FOCModulationType::SpaceVectorPWM;

  // contoller configuration
  // default parameters in defaults.h

  // motor.PID_current_q.P = 0.3f;
  // motor.PID_current_q.I = 1;
  // motor.PID_current_q.D = 0;
  // motor.PID_current_q.output_ramp = 100;
  // motor.LPF_current_q.Tf = 0.005f;

  // motor.PID_current_d.P = 0.3f;
  // motor.PID_current_d.I = 1;
  // motor.PID_current_d.D = 0;
  // motor.PID_current_d.output_ramp = 100;
  // motor.LPF_current_d.Tf = 0.1f;

  motor.PID_velocity.P = 0.2;
  motor.PID_velocity.I = 1;
  motor.PID_velocity.D = 0;
  motor.PID_velocity.output_ramp = 1000;
  motor.LPF_velocity.Tf = 0.01f;

  // angle P controller
  // motor.P_angle.P = 5;
  // motor.P_angle.I = 1;
  //  maximal velocity of the position control
  // motor.velocity_limit = 50;


  // use monitoring with serial
  Serial.begin(115200);
  // comment out if not needed
  motor.useMonitoring(Serial);

  motor.monitor_variables = _MON_TARGET | _MON_VEL | _MON_ANGLE; // default _MON_TARGET | _MON_VOLT_Q | _MON_VEL | _MON_ANGLE
  motor.monitor_downsample = 10000;
  motor.motion_downsample = 10;
    // initialize motor

  Serial.println(F("Motor init."));
  motor.init();

  // align encoder and start FOC
  // Serial.println(F("Motor align."));

  //motor.initFOC(3.14, Direction::CW);
  motor.initFOC();
  Serial.println(F("Init FOC"));

  // add target command T
  command.add('M', onMotor, "my motor");
  command.add('T', doTarget, "target");

  Serial.println(F("Motor ready."));
  _delay(1000);
}

void loop() {


  motor.loopFOC();

  motor.move(target);

  command.run();
  motor.monitor();
}

Here the VESC 6 hardware configuration file

#include <Arduino.h>

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

// PORT #1:  CAN  
// =============
#define CAN_RX        PB8
#define CAN_TX        PB9

// PORT #2: HALL, ENCODER, I2C1 or USART6
// ======================================
#define HALL_1        PC6
#define HALL_2        PC7
#define HALL_3        PC8
// OR
#define ENC_A         PC6
#define ENC_B         PC7
#define ENC_I         PC8
// OR
#define USART6_TX    PC6
#define USART6_RX    PC7
// AND
#define TEMP_MOTOR    PC4

// PORT #3: USART3, I2C2 or SPI1
// =============================
#define USART3_TX     PB10
#define USART3_RX     PB11
// OR
#define I2C2_SCL      PB10
#define I2C2_SDA      PB11
// OR
#define SPI1_NSS      PA4
#define SPI1_SCLK     PA5 
#define SPI1_MISO     PA6
#define SPI1_MOSI     PA7

// PORT #4: SWD
// ============
#define SWDIO         PA13
#define SWCLK         PA14

// PORT #5: SERVO
// ==============
#define SERVO         PB6

// PORT #6: USB
// ============
#define USB_DM        PA11
#define USB_DP        PA12

// #7: INTERNAL PINS
// =================
#define VOLTAGE_1     PA0
#define VOLTAGE_2     PA1
#define VOLTAGE_3     PA2
#define ADC_TEMP      PA3
#define H1            PA8
#define H2            PA9
#define H3            PA10
#define CURRENT_1     PC0
#define CURRENT_2     PC1
#define CURRENT_3     PC1
#define L1            PB13
#define L2            PB14
#define L3            PB15
#define AN_IN         PC3
#define LED_GREEN     PB0
#define LED_RED       PB1
#define EN_GATE       PB5
#define FAULT         PB7


const PinMap PinMap_PWM[] = {
  //  {PA_0,  TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 1, 0)}, // TIM2_CH1
  {PA_0,  TIM5,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM5, 1, 0)}, // TIM5_CH1
  //  {PA_1,  TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 2, 0)}, // TIM2_CH2
  {PA_1,  TIM5,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM5, 2, 0)}, // TIM5_CH2
  //  {PA_2,  TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 3, 0)}, // TIM2_CH3
  {PA_2,  TIM5,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM5, 3, 0)}, // TIM5_CH3
  //  {PA_2,  TIM9,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM9, 1, 0)}, // TIM9_CH1
  //  {PA_3,  TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 4, 0)}, // TIM2_CH4
  {PA_3,  TIM5,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM5, 4, 0)}, // TIM5_CH4
  //  {PA_3,  TIM9,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM9, 2, 0)}, // TIM9_CH2
  {PA_5,  TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 1, 0)}, // TIM2_CH1
  //  {PA_5,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 1, 1)}, // TIM8_CH1N
  //  {PA_6,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 1, 0)}, // TIM3_CH1
  {PA_6,  TIM13,  STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_TIM13, 1, 0)}, // TIM13_CH1
  //  {PA_7,  TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 1, 1)}, // TIM1_CH1N
  //  {PA_7,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 2, 0)}, // TIM3_CH2
  //  {PA_7,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 1, 1)}, // TIM8_CH1N
  {PA_7,  TIM14,  STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_TIM14, 1, 0)}, // TIM14_CH1
  {PA_8,  TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 1, 0)}, // TIM1_CH1
  {PA_9,  TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 2, 0)}, // TIM1_CH2
  {PA_10, TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 3, 0)}, // TIM1_CH3
  {PA_11, TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 4, 0)}, // TIM1_CH4
  {PA_15, TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 1, 0)}, // TIM2_CH1
  //  {PB_0,  TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 2, 1)}, // TIM1_CH2N
  //  {PB_0,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 3, 0)}, // TIM3_CH3
  {PB_0,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 2, 1)}, // TIM8_CH2N
  //  {PB_1,  TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 3, 1)}, // TIM1_CH3N
  //  {PB_1,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 4, 0)}, // TIM3_CH4
  {PB_1,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 3, 1)}, // TIM8_CH3N
  {PB_3,  TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 2, 0)}, // TIM2_CH2
  {PB_4,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 1, 0)}, // TIM3_CH1
  {PB_5,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 2, 0)}, // TIM3_CH2
  {PB_6,  TIM4,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM4, 1, 0)}, // TIM4_CH1
  {PB_7,  TIM4,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM4, 2, 0)}, // TIM4_CH2
  {PB_8,  TIM4,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM4, 3, 0)}, // TIM4_CH3
  //  {PB_8,  TIM10,  STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM10, 1, 0)}, // TIM10_CH1
  {PB_9,  TIM4,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM4, 4, 0)}, // TIM4_CH4
  //  {PB_9,  TIM11,  STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM11, 1, 0)}, // TIM11_CH1
  //  {PB_10, TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 3, 0)}, // TIM2_CH3
  //  {PB_11, TIM2,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM2, 4, 0)}, // TIM2_CH4
  {PB_13, TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 1, 1)}, // TIM1_CH1N
  {PB_14, TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 2, 1)}, // TIM1_CH2N
  //  {PB_14, TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 2, 1)}, // TIM8_CH2N
  // {PB_14, TIM12,  STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_TIM12, 1, 0)}, // TIM12_CH1
  {PB_15, TIM1,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF1_TIM1, 3, 1)}, // TIM1_CH3N
  //  {PB_15, TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 3, 1)}, // TIM8_CH3N
  // {PB_15, TIM12,  STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_TIM12, 2, 0)}, // TIM12_CH2
  {PC_6,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 1, 0)}, // TIM3_CH1
  //  {PC_6,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 1, 0)}, // TIM8_CH1
  //  {PC_7,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 2, 0)}, // TIM3_CH2
  {PC_7,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 2, 0)}, // TIM8_CH2
  {PC_8,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 3, 0)}, // TIM3_CH3
  //  {PC_8,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 3, 0)}, // TIM8_CH3
  //  {PC_9,  TIM3,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF2_TIM3, 4, 0)}, // TIM3_CH4
  {PC_9,  TIM8,   STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_TIM8, 4, 0)}, // TIM8_CH4
  {NC,    NP,    0}
};

Wow, nice work!

I would have said the STM32F40xxx - which is what should be on there according to the documentation from VESC, should actually do a bit more than 80r/s…

However the FlipSky models I can find on BangGood all have a big “QC passed” sticker covering the MCU. So the first question I would have is which MCU is actually on there on the VESC you have?

If it is an original STM32F40xxx then we should investigate why you’re not getting more speed out of it.

For this it would be good to know:

  • what clock speed is the MCU clocked at - are the clock settings matching the MCU an oscillator used?

  • what’s the KV rating of the motor you’re using?

  • did you try open-loop? If running in open loop but reading the speed with the hall sensors, what speeds can you get?

  • your PI values seem low, are they well tuned? Maybe you just need to bump them up a bit to get more speed?

Thanks @runger for the fast reply!

Yes, in fact remove that sticker it was the first step that i’ve made when the driver arrived :joy: i confirm that is a STM32F405RG and i’ve also give a try with the VESC stock firmware and i’ve tested untill 3000 rpm (so more or less 300 rad/sec) and it works without any problem.

As @Owen_Williams set on the hardware configuration file and also from this discussion of @JorgeMaker the MCU should work at 168 MHz (or 84 MHz anyway at max… i should follow the prescaler value for the specific TIM)

140KV so relatively lower, but there’s too much difference respect the VESC firmware…

Nope, tomorrow morning i’ll try, thanks for the advice!

No, are not well tuned, but with the commander i’ve tried to tune with no change. i’ll try again, i also suspect that can be correlated. What have made a big difference was rise the current limit, in fact before with 10 A the motor reached at max 20 rad/s even if the current never exceed 1/2 A… seems that the current limit affect the maximum speed… till a certain limit.

Hey @luzz94,

Since you are using the voltage torque mode, the code is not measuring the current.
So the current limiting is done throuh the voltage limiting in the worst case scenario (when the velocity is 0).

Wen you’ve set your current limit on 65Amps and your phase resistance is 0.053 Ohms.
Simplefoc will limit the voltage that can be set at

motor.voltage_limit = 0.053*65 = 3.4 V

So effectively you’ll be using only 3.4 out of your 24V.
However, when the motor spins with a ceraint velocity it generates the back-emf and at some point the difference in between the voltage you set and the back-emf generated is too low to accelerate your motor any further. So this voltage limit will in a way be directly limiting the motors maximal velocity.

So I’d suggest you to raise the current_limit in order to use all your 24V.

Thank you @Antun_Skuric for the crystal clear explanation! in fact this morning i’ve tried to change at first instance the current_limit parameter and i’ve reach more or less 150 rad/s. Over that range, the motor start to be too much noisy.

So now i’ll move to link the inline current sensing to the motor and try the FOC current mode… thank you guys!

Hey @luzz94

The VESC does not have inline current sensing only low-side if I’m not mistaking.

We currently do not support the low-side current sensing for now, but we are very close.
i am going to push a developemental version of the low-side current sensing for stm32f4 boards and VESC is one of them.
it would be really awesome if you would be interested in testing. :smiley:

The VESC 4 like the one that @Owen_Williams had tested it have the low-side current sensing in the two phase using the DRV8302 internal amplifiers. Instead the VESC 6 has the in-line current sensing with AD8418 in the tree phases and use the DRV8301.

Now you put me in doubt and it is better to check tomorrow if it physically mounts this amplifier to be 100% sure.

In any case one or two month ago i’ve tested the low-side current sensing in the B-G431B-ESC1 (if i remember correctly was in the dev branch) and effectively the “sound” and the behaviour of the motor was changed using the TorqueControlType::foc_current and FOCModulationType::SinePWM but as i was saying the motor still was like jerky and “sounds” bad (but less in this modality).

I have still in parallel this setup (B-G431B-ESC1 + unbranded motor) so i can compare the two! if I can make things work properly :sweat_smile:

Today i’ve tried to use the TorqueControlType::foc_current without success and to undestand what is going on i’ve tried to use the SimpleFOCStudio

@Antun_Skuric from current_sense.driverAlign() sometimes i get 3 and other one i get 4… can you suggest me a way to undestand which phase have the phase negative or undestand which sensing current on phases are swapped? in any case the fact that driverAlign() doesn’t give me the same result every time sound like that could be another problem, maybe due to the ADC configuration of the pins.

By inspecting the schematic, the datasheet and the firmware of the VESC 6 should be a gain of +20 for all the phases, let me know if i’m wrong.

Hey @luzz94,

Youre right!
Id does hevae inline current sense, I was absolutely unaware of that, so cool!

So looking into the shematic, all of your channels should be positive and no alignement is really necessary.

I you define your driver as:

BLDCDriver6PWM driver = BLDCDriver6PWM(PA8,PB13,PA9,PB14,PA10,PB15, PB5);
....
InlineCurrentSense current_sense = InlineCurrentSense(0.0005, 20, PC0,PC1,PC2);
...

Also, make sure to first call the driver.init() before calling the current_sense.init() .

So to skip the align procedure, you can do something like this:

....
motor.init()

current_sense.skip_align = true;
motor.initFOC();
...

If you doubt that your gains are inverted you can try inverting the gains manually (they all have the same gain in the specs (either positive or negative))

....
motor.init()

current_sense.skip_align = true;
current_sense.gain_a *= -1;
current_sense.gain_b *= -1 ;
current_sense.gain_c *= -1;
motor.initFOC();
...

Thanks! it was what i’ve done, by double check the port of the MCU that you suggest, i’ve found the error and was a typo here in the board_vesc_6.h that i’ve created:

Now the currentSense.driverAlign() give me sometimes 1 and another one 2 so still i don’t know why this behaviour but now i’m pretty sure about the configuration and i’ve putted as you suggest me, current_sense.skip_align = true;

The motor in the TorqueControlType::foc_current modality it’s very noisy, but i’m confident that is due to the PID values.

Going back to the good advice of @runger i’ve re-started from the velocity_openloop and in this modality by using the voltage limit instead of the resistance (even at very low current) the motor absorb a lot of current, by sending a target it can spin untill 10 rad/s (very low) and then it stuck.

Also this modality the motor sound little “strange” like a stepper motor and if i touch the flange i can feel some vibration. So i’m ever more convinced that this behavior is caracteristic of the SimpleFOC algorithms and not due to the PID tuning.

By a step forward, using the Hall sensors and the TorqueControlType::voltage i can reach very high speed and still the “strange” behaviour still hold. But apart that i’m really happy about the SimpleFOC library, very fast and easy implementations! it is suitable for the actual requirements of the project.

I think that maybe this strange behaviour appear with those type of motor (very low resistance and high torque and low pole pairs!) and i’m curious to test a smaller one to see if this theory it is true.

In fact i found lot of videos with small motors but no one with motors for skateboard like in VESC firmware.

I hope to have an oscilloscope as far as possible to undestand what is going on the three phases and if this jerk is related to some disturbances.

Moving on, I would love it if you could give me some advice on the position control implementation, in particular I am thinking of using the GenericSensor class as I think it is perfect for my application (vey thanks to add recently this in the library!). This is the configuration we are developing for a joint of the exoskeleton (img taken here):

So in my case the motor is a Flipsky 6354, the driver an FSVESC 6.7 (VESC 6) and the Harmonic drive is a Laifual drive that can provide an output rated torque of 50 Nm and 100 Nm of peak torque (2000 RPM on input) it provide a reduction ratio of 100 (so at the limb 20 RPM).

By focusing on the position sensors i would like to use for the velocity loop the hall sensors placed in the motor. Instead for the position loop i would like to use only the position sensor placed after the reduction that for now is a potentiometer (1$) or in the final implementation a particular magnetic sensor (60$) that can provide a ratiometric output or PWM or also CAN bus i think.

This choice is due to the fact that the joints cannot rotate more than 180 degree more or less, due to the limbs DoF and for the safety mechanical end stop (and also by software). Moreover the output speed is very low and the position requirements is not strict.

So the question is: in your opinion sound reasonable to use the genericSensor class? and there is a way to link to the velocity loop only the hall sensor and pass the custom position sensor only to the position loop?

If it is not possible i was thinking to use the same structure used for the PID of the position in SimpleFOC (PID library of arduino?) and close the loop “externally” in the void loop() by passing the genericSensor object created, it make sense?

All of this makes sense.You can certainly use the GenericSensor class.

Multiple sensors for the different loops are not supported in the library, though you could probably “hack up” the code to support it quite quickly.

We generally advise people to implement motion control “on top of” the existing control loops, so externally like you say. And the filter and PID classes can certainly be used to help do this.

Thank you @runger for the feedback! i’ll keep improve the controller performance and i’ll try to use the GenericSensor class and the outern position control as soon as possible.