1. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Download Package Now!
  2. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  3. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant here. Do not following these rules will lead to permanent exclusion from this website: Read the forum rules.

Showroom FlyPT - 6DOF Brushless DIY Actuators

Discussion in 'DIY Motion Simulator Projects' started by pmvcda, Aug 29, 2017.

  1. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    261
    Location:
    Portugal
    Balance:
    1,864Coins
    Ratings:
    +312 / 1 / -0
    My Motion Simulator:
    6DOF
    If you can do them in aluminium/steel, it's safer.
    I still didn't break one, but I don't want to be held responsible for any accident. :think
  2. minsu

    minsu New Member

    Joined:
    Feb 9, 2019
    Messages:
    4
    Balance:
    25Coins
    Ratings:
    +0 / 0 / -0
    My Motion Simulator:
    Arduino
    Sure. You don't have responsibility about them.
    I wondered if the 3D printed parts worked well.:thumbs
  3. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    261
    Location:
    Portugal
    Balance:
    1,864Coins
    Ratings:
    +312 / 1 / -0
    My Motion Simulator:
    6DOF
    Just received this on the mail:

    WP_20190212_12_13_49_Pro.jpg

    Expect some changes soon.
    Not only in hardware, but also on the interface... ;)
    • Like Like x 2
  4. SilentChill

    SilentChill Problem Maker SimTools 2.0 Beta Tester

    Joined:
    Jul 19, 2014
    Messages:
    2,280
    Occupation:
    Railway Maintenance
    Location:
    Morecambe, Lancashire, England
    Balance:
    18,247Coins
    Ratings:
    +3,003 / 29 / -0
    My Motion Simulator:
    DC motor, Arduino, Motion platform, 6DOF
    Is that for an all in one controller of some sort ?
  5. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    261
    Location:
    Portugal
    Balance:
    1,864Coins
    Ratings:
    +312 / 1 / -0
    My Motion Simulator:
    6DOF
    Well, the idea is to replace both ESP32. That way, I can calculate speed on the board instead of the interface.
    After testing a new code, with speed calculation on the ESP32 boards, everything seems smoother, but since there's no connection between both boards, I can't calculate speed as I wished.
    It's possible to connect both, but I'm feeling lazy for that, and the STM32 opens new opportunities.
    I might add wind and if possible seat flaps (at the speed I implement things, it will take some time...). Let's see what is available after controlling the hexapod.

    By the way, this is the code I tested with the ESP32:
    Code:
    // ============================
    // DIY 6DOF Motion Sim by FlyPT
    // ============================
    // SimTools2 ESP32 code to control actuators
    // Actuators use brushless motors controlled by BLDC8015A drivers
    // Position is obtained from internall hall sensors
    // This code is made to handle 3 actuators in one board and also made to support 6 in two boards.
    //
    // This code uses the FlyPT Hexapod Interface in standard serial mode without receiving speed or sending current position.
    // It just receives position and everything will be handled internally.
    // So, it's also possible to use this directly with Simtools.
    
    
    
    // ==================================
    // Current: Version 7 from 11/02/2019
    // ==================================
    
    
    
    // ==================================
    // SimTools2 configuration should be:
    // ==================================
    // Interface settings:
    // Interface Type: Serial
    // ComPort: The one used by your ESP32
    // BitsPerSec: 115200 (You can change the define to use another speed)
    // Data Bits: 8
    // Parity: None
    // Stop Bits: 1
    // Output - Bit Range: 10
    // Output - Type: Binary
    // Startup - Output: -
    // HW Start: -
    // Interface output for board 1: [1<Axis1a>][2<Axis2a>][3<Axis3a>]
    // Interface output for board 2: [4<Axis4a>][5<Axis5a>][6<Axis6a>]
    // Output Rate: 10ms
    // Shutdown - Output: -
    // HW Stop: -
    
    
    
    // =======
    // Defines
    // =======
    #define SERIAL_SPEED 115200 // Define to set up the serial speed comunication
    #define CALIBRATION_SPEED 50 // Speed used to search for minimum and maximum values
    #define UP HIGH // Define to set up direction as HIGH
    #define DOWN LOW // Define to set down direction as LOW
    #define PWM_RESOLUTION 8
    #define PWM_FREQUENCY 5000
    #define LIMITS_PIN 0           // Pin to capture position from pot wich defines the limits
    #define HALL_1_PIN 1           // Pin used for hall 1 interrupt
    #define HALL_2_PIN 2           // Pin used for hall 2 interrupt
    #define HALL_3_PIN 3           // Pin used for hall 3 interrupt
    #define LAST_ANGLE 4           // Last angle that was detected
    #define LAST_SUM 5             // Last sum to know last direction it took
    #define DIRECTION_PIN 6        // Pin to se direction
    #define SPEED_PIN 7            // Pin to set speed with PWM
    #define PWM_CHANNEL 8          // Channel used to generate PWM
    #define MINIMUM_POSITION 9     // Minimum position
    #define MAXIMUM_POSITION 10    // Maximum position
    #define CURRENT_POSITION 11    // Current position
    #define PRETENDED_POSITION 12  // Pretended position
    #define P_PID 13               // P from PID
    #define I_PID 14               // I from PID
    #define D_PID 15               // D from PID
    #define INTEGRATED_ERROR 16    // Sum of differences between pretended and current position
    #define LAST_ERROR 17          // Last difference between pretended and current position
    #define SPEED 18               // Speed of motor rotation
    #define FOUND 19               // For help. Used for example in actuator calibration
    #define NUMBERACTUATORS 3      // Number of actuators (for one board)
    #define P 256
    #define I 0
    #define D 100
    
    
    
    // ======================
    // Actuators information:
    // ======================
    // 0  - LIMITS_PIN          - Pin to capture position from pot
    // 1  - HALL_1_PIN          - Pin used for hall 1 interrupt
    // 2  - HALL_2_PIN          - Pin used for hall 2 interrupt
    // 3  - HALL_3_PIN          - Pin used for hall 3 interrupt
    // 4  - LAST_ANGLE          - Last angle that was detected
    // 5  - LAST_SUM            - Last sum to know last direction it took
    // 6  - DIRECTION_PIN       - Pin to se direction
    // 7  - SPEED_PIN           - Pin to set speed with PWM
    // 8  - PWM_CHANNEL         - Channel used to generate PWM
    // 9  - MINIMUM_POSITION    - Minimum position
    // 10 - MAXIMUM_POSITION    - Maximum position
    // 11 - CURRENT_POSITION    - Current position
    // 12 - PRETENDED_POSITION  - Pretended position
    // 13 - P_PID               - P from PID
    // 14 - I_PID               - I from PID
    // 15 - D_PID               - D from PID
    // 16 - INTEGRATED_ERROR    - Sum of differences between pretended and current position
    // 17 - LAST_ERROR          - Last difference between pretended and current position
    // 18 - SPEED               - Speed of motor rotation
    // 19 - FOUND               - Used as help for calibraion
    static int actuator[3][20] ={   {  4, 27, 26, 25,  5,  0, 32, 33,  0, -1, -1, 0, 0, P, I, D, 0, 0, 0, 0 },
                                    { 36, 35, 34, 39,  5,  0, 18,  5,  1, -1, -1, 0, 0, P, I, D, 0, 0, 0, 0 },      
                                    { 15, 23, 22, 21,  5,  0, 19, 17,  2, -1, -1, 0, 0, P, I, D, 0, 0, 0, 0 } };                    
    static int bufferCurrent = 0; // To hold current read fom SimTools command
    static int bufferCount = -1; // To hold position in bufferCommand array
    static int bufferCommand[5] = {0}; // To hold command info from SimTools
    // Maps to get increment off position from comparing previous hall sensor state with current state
    // Vertical, the new value, horizontal, the old one
    // Stores for 0 and 7, althought they should not ocurr, digital read might get them. They are ignored with zero change.
    // State sequence going up
    // 5=101
    // 1=001
    // 3=011
    // 2=010
    // 6=110
    // 4=100
    // First index for new state, second for old state ([new][old])
    static int slowSpeedUp[8][8] =      { { 0,  0,  0,  0,  0,  0,  0, 0},
                                          { 0,  0, -2, -1,  2,  1,  3, 0},
                                          { 0,  2,  0,  1, -2,  3, -1, 0},
                                          { 0,  1, -1,  0,  3,  2, -2, 0},
                                          { 0, -2,  2,  3,  0, -1,  1, 0},
                                          { 0, -1,  3, -2,  1,  0,  2, 0},
                                          { 0,  3,  1,  2, -1, -2,  0, 0},
                                          { 0,  0,  0,  0,  0,  0,  0, 0} };
                      
    static int slowSpeedDown[8][8] =    { { 0,  0,  0,  0,  0,  0,  0, 0},
                                          { 0,  0, -2, -1,  2,  1, -3, 0},
                                          { 0,  2,  0,  1, -2, -3, -1, 0},
                                          { 0,  1, -1,  0, -3,  2, -2, 0},
                                          { 0, -2,  2, -3,  0, -1,  1, 0},
                                          { 0, -1, -3, -2,  1,  0,  2, 0},
                                          { 0, -3,  1,  2, -1, -2,  0, 0},
                                          { 0,  0,  0,  0,  0,  0,  0, 0} };
    
    
                                                      
    // ==============
    // Initialization
    // ==============
    void setup()
    {
      // Setup communication
      Serial.begin(115200, SERIAL_8N1);
    
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        // Initialize pins
        pinMode(actuator[n][LIMITS_PIN], INPUT);
        pinMode(actuator[n][HALL_1_PIN], INPUT);
        pinMode(actuator[n][HALL_2_PIN], INPUT);
        pinMode(actuator[n][HALL_3_PIN], INPUT);
        pinMode(actuator[n][DIRECTION_PIN], OUTPUT);
        pinMode(actuator[n][SPEED_PIN], OUTPUT);
    
        // Setup PWM
        ledcSetup(actuator[n][PWM_CHANNEL], PWM_FREQUENCY, PWM_RESOLUTION);
        ledcAttachPin(actuator[n][SPEED_PIN], actuator[n][PWM_CHANNEL]);
        ledcWrite(actuator[n][PWM_CHANNEL], 0);
      }
    
      // Initialize pin to light up onboard led
      pinMode(2, OUTPUT);
      // Countdown of 5 seconds before calibration, making internal led flash at half a second
      for(byte n=5; n>0; n--)
      {
        delay(500);
        digitalWrite(2, HIGH);
        delay(500);
        digitalWrite(2, LOW);
      }
      digitalWrite(2, LOW);
    
      // Calibrate actuators, searching for minimums and maximums
      calibrate();
      // Put actuators in the middle after calibration
      for(int n=0;n<NUMBERACTUATORS;n++) actuator[n][PRETENDED_POSITION]=actuator[n][MAXIMUM_POSITION]/2;
      // Create thread that gets position from hall sensors to run in the core 0
      // It will run in paralell with the main loop that uses core 1 by default
      xTaskCreatePinnedToCore(
        TaskUpdatePositions,  // Task function
        "UpdatePositions",    // Name    
        10000,                // Stack size
        NULL,                 // Task input parameter
        1,                    // Priority of the task
        NULL,                 // Task handle
        0);                   // Core
    }
    // ======================
    // Update actuator action
    // ======================
    // Here we calculate the PID depending of desired vs current position and send respective signal to motor controller
    // Definitions to avoid repeated definitions in functions
    static int limitValue=1023;
    static int error=0;
    static int p=0;
    static int i=0;
    static int d=0;
    void updateActuator(byte n)
    {
      // Calculate pid
      // =============
      error=actuator[n][PRETENDED_POSITION]-actuator[n][CURRENT_POSITION];
      //  actuator[n][INTEGRATED_ERROR]+=error;  // <<<<<<< commented for speed, not in use (i from pid)
      p=actuator[n][P_PID] * error;
      //  i=actuator[n][I_PID] * constrain(actuator[n][INTEGRATED_ERROR], -100, 100); // <<<<<<< commented for speed, not in use (i from pid)
      d=actuator[n][D_PID] * (error - actuator[n][LAST_ERROR]);
      actuator[n][LAST_ERROR]=error;
      //  actuator[n][SPEED]=constrain((p+i+d)/100, -255, 255);  // <<<<<<< commented for speed, not in use (i from pid)
      actuator[n][SPEED]=FlyPTConstrain((p+d)>>7, -255, 255); // 2 4 8 16 32 64 128 Divide by 128
      // Apply changes to motor controller
      // =================================
      if(actuator[n][SPEED]>0)
      {
        ledcWrite(actuator[n][PWM_CHANNEL], actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], UP);
      }
      else
      {
        ledcWrite(actuator[n][PWM_CHANNEL], -actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], DOWN);
      }
      // Check limits
      // ============
      // Adjust position if minimum or maximum switch is activated in case of a drift in position calculation
      // WITH A FIXED RESISTANCE IT WILL BE CHANGE TO A RANGE WITH ONLY 1 READ and 2 IF'S
      limitValue=(analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN]))>>2;
      // If they are actuated, recalibrate position
      if(limitValue>=4095) actuator[n][CURRENT_POSITION]=actuator[n][MAXIMUM_POSITION];    // We are at maximum position
      else if (limitValue<=0) actuator[n][CURRENT_POSITION]=actuator[n][MINIMUM_POSITION]; // We are at minimum position
    }
    
    // ============================================================================================================================
    // REPLACEMENT FOR CONSTRAIN
    // ============================================================================================================================
    // Problems using constrain with negative numbers and serial.read ... ???
    int FlyPTConstrain(int value, int low, int high)
    {
      return (value < low) ? low : (value > high) ? high : value;
    }
    
    // ====================================
    // Update current position of actuators
    // ====================================
    // Definitions to avoid repeated definitions in functions
    static int angle=0;
    void updatePositions(byte n)
    {
        angle=digitalRead(actuator[n][HALL_1_PIN])*4+digitalRead(actuator[n][HALL_2_PIN])*2+digitalRead(actuator[n][HALL_3_PIN]);
        if (actuator[n][LAST_SUM]>0) actuator[n][LAST_SUM]=slowSpeedUp[angle][actuator[n][LAST_ANGLE]];
        else actuator[n][LAST_SUM]=slowSpeedDown[angle][actuator[n][LAST_ANGLE]];
        actuator[n][LAST_ANGLE]=angle;
        actuator[n][CURRENT_POSITION]+=actuator[n][LAST_SUM];
    }
    // =============================
    // Get information from SimTools
    // =============================
    void getSimToolsCommands()
    {
      // Look for start and finish of a command on the serial
      // When we have a command send it for analyze
      if (Serial.available()) // Using if instead of while to avoid the code to stop here for to long
      {
        bufferCurrent = Serial.read();
        if (bufferCount == -1)
        {
          if (bufferCurrent != '[') bufferCount = -1;
          else bufferCount = 0;
        }
        else
        {
          bufferCommand[bufferCount] = bufferCurrent;
          bufferCount++;
          if (bufferCount > 3)
          {
            if (bufferCommand[3] == ']') analyzeSimToolsCommand();
            bufferCount = -1;
          }
        }
      }
    }
    // ================================
    // Analyze command sent by SimTools
    // ================================
    void analyzeSimToolsCommand()
    {
      // Analyze command, see if it's a command and wich actuator is affected
      // Since we need to use 2 boards for 6 actuators, there are two cases
      // So board 1 is used for actuator 1, 3 and 5 and board 2 for 2, 4 and 6
      // For board 1, actuators 1, 3 and 5
      // actuator 1 = index 0
      // actuator 3 = index 1
      // actuator 5 = index 2
      // For board 2, actuators 2, 4 and 6
      // actuator 2 = index 0
      // actuator 4 = index 1
      // actuator 6 = index 2
      switch (bufferCommand[0])
      {
        case '1':
        case '4':
          actuator[0][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[0][MINIMUM_POSITION], actuator[0][MAXIMUM_POSITION]);
          break;
        case '2':
        case '5':
          actuator[1][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[1][MINIMUM_POSITION], actuator[1][MAXIMUM_POSITION]);
          break;
        case '3':
        case '6':
          actuator[2][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[2][MINIMUM_POSITION], actuator[2][MAXIMUM_POSITION]);
          break;
        default:
          return;
          break;
      }
    }
    // =========
    // Calibrate
    // =========
    void calibrate()
    {
      // Move all actuators away from the minimum until we have no minimum switch return
      // ===============================================================================
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN],UP);
        ledcWrite(actuator[n][PWM_CHANNEL], CALIBRATION_SPEED);
        actuator[n][FOUND]=false;
      }
      int found=0;
      while (found<NUMBERACTUATORS)
      {
        for(int n=0;n<NUMBERACTUATORS;n++)
        {
          // Read limit switch position (we are looking for more than zero)
          // We read more than one time to be sure (4 times in this case)
          if(!actuator[n][FOUND] && analogRead(actuator[n][LIMITS_PIN])>0 && analogRead(actuator[n][LIMITS_PIN])>0 && analogRead(actuator[n][LIMITS_PIN])>0 && analogRead(actuator[n][LIMITS_PIN])>0)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            actuator[n][FOUND]=true;
            found++;
          }
        }
      }
    
      // Move all actuators to minimum
      // =============================
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN],DOWN);
        ledcWrite(actuator[n][PWM_CHANNEL], CALIBRATION_SPEED);
        actuator[n][FOUND]=false;
      }
      found=0;
      while (found<NUMBERACTUATORS)
      {
        for(int n=0;n<NUMBERACTUATORS;n++)
        {
          // Read limit switch position (we are looking for zero)
          // We read more than one time to be sure (4 times in this case)
          if(!actuator[n][FOUND] && analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])<=0)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            actuator[n][MINIMUM_POSITION]=0;
            actuator[n][CURRENT_POSITION]=0;
            actuator[n][FOUND]=true;
            found++;
          }
        }
      }
      // Search for maximums
      // ===================
      for(int n=0;n<NUMBERACTUATORS;n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN],UP);
        ledcWrite(actuator[n][PWM_CHANNEL], CALIBRATION_SPEED);
        actuator[n][FOUND]=false;
      }
      found=0;
      while (found<NUMBERACTUATORS)
      {
        for(int n=0;n<NUMBERACTUATORS;n++)
        {
          // Update position of actuator
          updatePositions(n);
          // Read limit switch position (we are looking for big value 4095)
          if(!actuator[n][FOUND] && (analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN])+analogRead(actuator[n][LIMITS_PIN]))/4>=4095)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            updatePositions(n);
            actuator[n][MAXIMUM_POSITION]=actuator[n][CURRENT_POSITION]/2;
            actuator[n][MINIMUM_POSITION]=-actuator[n][MAXIMUM_POSITION];
            actuator[n][FOUND]=true;
            found++;
          }
        }
      }
    }
    // =========
    // Main loop
    // =========
    // Here we get the simtools commands and make the actuator move
    void loop()
    {
        getSimToolsCommands();
        for(int n=0;n<NUMBERACTUATORS;n++) updateActuator(n);
    }
    // ============================
    // Update positions thread loop
    // ============================
    // This thread updates the actuators positions reading the hall sensors
    void TaskUpdatePositions(void * parameter)
    {
      while (true)
      {
        delay(1); // To give ESP32 interrupts a chance to run or they will generate a boot
        for(int n=0;n<NUMBERACTUATORS;n++) updatePositions(n);
      }
    }
    
    This code works like using SMC.
    We just send the pretended position of the actuator and PID is handled on the ESP boards.
    Serial settings on the interface/Simtools, should be:

    Sem nome.jpg

    I will add this solution on the first page of the thread when I can.
    • Informative Informative x 2
    • Creative Creative x 1