1. Do not share user accounts! Any account that is shared by another person will be blocked and closed. This means: we will close not only the account that is shared, but also the main account of the user who uses another person's account. We have the ability to detect account sharing, so please do not try to cheat the system. This action will take place on 04/18/2023. Read all forum rules.
    Dismiss Notice
  2. 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 Now a Download Plan!
  3. 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
  4. 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. Do not follow these rules can lead to permanent exclusion from this website: Read the forum rules.
    Are you a company? Read our company 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:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Before reading everything here, just a short info:



    Hi,

    First time posting, but sneaking for a long long time (sorry for some English errors I might post).
    So some of the guys here are guilty! Yes, I started to build a 6DOF motion simulator with DIY actuators.
    SeatTimes, SilentChill and some others, you are an inspiration.
    I think my wife hates you guys... ;)

    So, for some years I've been looking at motion sims, but since my desire was always cars and air planes, I always thought that less than 6DOF would kill the aircraft experience (probably I'm wrong).
    But now with so many 6DOF builds, specially with actuators, I though it's the time to start my build.

    Well it already started some months ago, but free time is always a problem.
    I will make some posts to put at the start of this thread the current project development.
    I will make the following topics:
    -Videos and photos (latest images and videos of the construction)
    -Parts (list of all parts, and where I got them)
    -Plans (drawings, schematics and 3D printed parts files)
    -Software (software used, setups and code developed for this project)

    So let's start! Hope my project can help others. And let's hope I can make it.


    (FlyPT is the user I usually use in other forums, but I had it banned here, probably because I was not active for a long time?)
    • Like Like x 10
    • Agree Agree x 1
    • Winner Winner x 1
    • Creative Creative x 1
    Last edited: Mar 13, 2019
  2. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Videos and photos
    latest images and videos of the construction

    Current state (needs cable management...):
    IMG_20200102_104713.jpg
    IMG_20200102_104655.jpg
    IMG_20200102_104725.jpg


    Older state, just the hexapod:
    WP_20180924_18_58_24_Rich.jpg

    Latest video
    Testing FlyPT Mover with DiRT Rally2:


    Testing the old interface between SimTools and ESP32 boards:


    Fifth video
    With load:


    Fourth video
    Hexapod moving.


    Third test video
    Arduino replaced by ESP32 and no more potentiometers.
    Positioning made with internal hall sensors of BLDC motor.


    Older videos in my Youtube Channel...
    • Like Like x 12
    • Winner Winner x 4
    • Agree Agree x 1
    Last edited: Jan 24, 2020
  3. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Parts
    List of all parts, and where I got them
    No prices, because they are always changing, I don't want to know how much I spend in total :p

    Motors (6x):
    Nanotec.jpg
    Nanotec DB59C024035-A (brushless).
    1 Bought from Portuguese reseller
    5 Bought at Farnell (http://pt.farnell.com/nanotec/db59c024035-a/bldc-motor-24vdc-4500rpm/dp/2507576 - less than half the price from above )
    Specs: http://en.nanotec.com/fileadmin/files/Datenblaetter/BLDC/DB59/DB59C024035-A.pdf

    Motor drivers (6x):
    BLDC-8015A.jpg
    ACT Motor BLDC-8015A
    Bought on ebay (http://www.ebay.co.uk/itm/252468509819?_trksid=p2060353.m1438.l2649&ssPageName=STRK:MEBIDX:IT).
    Specs: http://www.wantmotor.com/product/8015a.html (From Wantai, but equal to ACT Motor)
    ATTENTION: ACT / Wantai / Longs motors seems the same, but try to buy all from the same source. I got from Wantai and ACT, and controllers have different speeds! <<< It's possible to adjust speed on those controllers, but you have to open them. Post about that: https://www.xsimulator.net/communit...shless-diy-actuators.10799/page-8#post-166235

    Power supply (1x):
    Mean Well.jpg
    Mean Well RSP-3000-24
    Bought on ebay (http://www.ebay.co.uk/itm/231473165159?_trksid=p2060353.m2749.l2649&var=530732464572&ssPageName=STRK:MEBIDX:IT).
    Specs: http://media.it-tronics.de/Datasheets/Power_Supplies/MeanWell/RSP-3000.pdf

    Microswitches (12x):
    Microswitch.jpg
    Bought on ebay (http://www.ebay.co.uk/itm/282088135235?_trksid=p2060353.m2749.l2649&var=581040167277&ssPageName=STRK:MEBIDX:IT)
    Specs: 16mm lever

    Emergency stop buttons (2x):
    p.jpg
    Bought on ebay (https://www.ebay.fr/itm/Push-Button...069214?hash=item1cb45cdd9e:g:Lg4AAOSw4A5YpGpe)

    Aluminium tube (6x):
    Aluminium tube.jpg
    Bought from Motedis (http://www.motedis.com/shop/Tube-sy...ular-tube-aluminium-anodized-28x2mm::823.html)
    Specs: 28mm exterior, 2mm wall with 500mm length

    Aluminium profile (12x):
    Aluminium profile.JPG
    Bought from Motedis (http://www.motedis.com/shop/Slot-pr...-slot-5/Profile-20x60-I-Type-slot-5::499.html)
    Specs: 20x60 I-Type slot 5 with 730mm lenght

    Aluminium for upper structure with 40x40mm (5m)

    Aluminium for bottom structure with 80x40mm (5m)

    Seat (1x):
    p.jpg
    Caterham Seven/7/R500 fiberglass seat
    Bought on ebay (https://www.ebay.co.uk/p/Caterham-S...breglass-Bucket-Seat-in-Black-SE81/1042847988)

    Ballscrew (6x):
    Ball screw.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...es/Ball-screw/Ballscrew-SFU1605-DM::1444.html)
    Specs: SFU1605-DM with 542mm (5mm step for each turn with 16mm diameter)

    Nut for ballscrew (6x):
    Ball screw nut.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...ball-screw/Screw-nut-DIN-439-M10x1::2444.html)
    Specs: DIN 439 M10x1

    3D Filament PET-G 1,75mm black (4kg):
    PETG.jpg
    Bought from Motedis (http://www.motedis.com/shop/table_cats.php?table_id=45)

    Flange Bearing (6x)
    Bearing.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...lange-Bearing-10mm-die-cast-KFL000::5212.html)
    Specs: 10mm die-cast aluminium KFL000

    Plain bearing (6x):
    Igus.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...g/Plain-bearing-Igus-GFM-283239-20::1166.html)
    Specs: Igus GFM-283239-20 Type 283239-20

    Flexible clutch (6x):
    Clutch.jpg
    Bought from ebay(http://www.ebay.co.uk/itm/Wellenkup...287275?hash=item3f76fdd8eb:g:MIAAAOSwOgdYynFj)
    Specs: Diameter 25mm Lenght 30mm Shaft diameter 8,00mm/8,00mm

    Universal joint (12x):
    UJoint.jpg
    Bought from ebay (http://www.ebay.co.uk/itm/12mm-Dia-...654174&hash=item2cadada585:g:odAAAOSwuhhXWW89)
    Specs: 12mm Dia Shaft Coupling Motor Connector

    Screws:
    Socket cap Allen screw M4 x 12mm - To fix limit switches (6 x 4x = 24x)
    Socket cap Allen screw M5 x 20mm - Most used screw (6 x 18x = 108x)
    Flat slotted screw M5 x 50mm - To hold bottom of actuator (6 x 4x = 24x)
    Hex screw M6 x 25mm - To join motor mount and plain bearing (6 x 2x = 24x)
    Hex screw M12 x 25mm - To join upper structure to actuators (6x)
    Flat Allen screw M5x30 (6 x 4x = 24x)
    Flat slotted screw M5x50 (6 x 4x = 24x)
    Cheese head slotted screw M5x60 (6 x 9x = 54x)

    Nuts:
    Retaining nuts for M5 (6 x 8x = 48x)
    Retaining nuts for M6 (6 x 2x = 12x)

    Washers:
    For M5 (6 x 12x = 72x)
    For M6 (6 x 4x = 24x)
    For M12 with 32mm diameter (6x)
    For M12 with 24mm diameter (6 x 2x = 12x)

    D Sub Connectors (6x):
    2294339-40.jpg 2294341-40.jpg 633860-40.jpg
    Bought at Farnell:
    Male: http://pt.farnell.com/norcomp/681s13w3103l001/d-sub-combo-plug-40amp-13way/dp/2294355?st=d sub
    Female: http://pt.farnell.com/norcomp/680s13w3103l401/d-sub-combo-plug-40amp-13way/dp/2294339?st=d sub
    Housing: http://pt.farnell.com/mh-connectors/mhd45zk-25-k/backshell-d-nickel-45dg-25way/dp/633860
    Specs: 3 power pins up to 40A and 10 signal pins up to 5A.

    Wires:
    0.35 mm2 cross section for signal (various colours)
    1.00 mm2 cross section for motors power (yellow, black and red)
    s-l1600 (3).jpg
    Bought on ebay (https://www.ebay.fr/itm/Automotive-Wire-0-35-1-5-mm-Thin-Wall-Cable-AWG16-22-Singlecore-5m-10m-lead/282093003064?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    Diode to protect PSU (6x):
    s-l500.jpg
    Bought on ebay (https://www.ebay.fr/itm/P1000M-10A1...-10/182909329114?_trksid=p2485497.m4902.l9144)
    Specs: P1000M 10A10 1000V 10A

    Relay 24V 200A (1x):
    s-l1600.jpg
    Bought on ebay (https://www.ebay.fr/itm/Car-Truck-Boat-24V-200A-Heavy-Duty-Split-Charge-Starter-Relay-with-Teminal/163238356171?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    ESP32 Dev Kit (2x):
    s-l1600.jpg
    Bought on ebay (https://www.ebay.fr/itm/Espressif-ESP32-WLAN-Dev-Kit-Board-Development-Bluetooth-Wifi-v1-WROOM32-NodeMCU/253059783728?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    Level shifters (10x):
    p.jpg
    Bought on ebay (https://www.ebay.fr/itm/5-Stuck-4-K...668603?hash=item3aeafec63b:g:xR0AAOSwiYlaD2Gc)

    Male/Female connectors with 10 pins to mount on PCB (6x):
    s-l1600 (1).jpg
    Bought on ebay (https://www.ebay.fr/itm/KIT-5x-BUCHSE-STECKER-10-polig-CRIMPKONTAKTE-2-54mm-PCB-Gerade-A2011/253322647034?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    Male/Female pins to mount ESP32 and level shifters on the PCB (10x M/F with 6 pins and 4x M/F with 15 pins):
    s-l1600 (2).jpg
    Bought on ebay (https://www.ebay.fr/itm/Pin-or-Female-Header-Edge-Pins-Strip-0-1-2-54mm-for-Breadboard-PCB-for-Dupont/263333424929?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)

    PCB board 5x7 (2x):
    s-l500.jpg
    Bought on ebay (https://www.ebay.fr/itm/1055-Double-Side-Copper-prototype-pcb-Universal-Board-for-Arduino-au-choix/292031954800?ssPageName=STRK:MEBIDX:IT&_trksid=p2057872.m2749.l2649)




    PARTS SPECIFIC FOR POT POSITIONING VERSION
    Potentiometers (6x):
    4434310.jpg
    Vishay 10K 10 turn
    Bought at Farnell (http://pt.farnell.com/vishay/536-1-1-103/wirewound-potentiometer-10kohm/dp/1386483?st=vishay pot)
    Specs: http://www.farnell.com/datasheets/2...MI_oTz8b3L2wIVhPdRCh08AQzXEAAYAiAAEgLrj_D_BwE

    Idler wheel for 6mm wide belt (6x):
    Idler wheel.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...-6mm/Idler-wheel-for-6mm-wide-belt::4523.html)
    Specs: 6mm wide with 18mm outside diameter

    Aluminium pulley (6x):
    6mm GT2 pulley with 24 teeth.jpg
    Bought on ebay (http://www.ebay.co.uk/itm/152084840946?_trksid=p2060353.m2749.l2649&var=451287689730&ssPageName=STRK:MEBIDX:IT)
    Specs: 24 teeth pulley for 6mm GT2 belt

    6mm GT2 belt (10m):
    6mm GT2 belt.jpg
    Bought from Motedis (http://www.motedis.com/shop/Dynamic...mm/GT2-Open-Timing-Belt-6mm-1000mm::4251.html)
    Specs: 6mm GT2 belt
    • Informative Informative x 3
    • Like Like x 2
    Last edited: Jan 24, 2020
  4. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Cost £££ $$$ €€€
    Cost for building this motion sim

    I thought of not making this, but here's a current look at the costs for the current version using hall sensors from the motors:

    Sem nome.jpg

    Those costs are only for:
    -The hexapod, no chair or wheel support.
    -No delivery costs (depends on where you are)

    I spent more with errors I made and building a pot version of the actuators.
    Parts are from Farnell, Motedis and ebay. You can see the list in a post above.

    Could have bought a pack of motor and controller for 98,99€:
    s-l1600.jpg
    BLDC 8015A + 57BLF03 (https://www.ebay.fr/itm/ACT-MOTOR-G...552331?hash=item3afed62d4b:g:ZU8AAOSw0hlZMXao)
    Motor is slower, but cost would come down to 2636,58€.





    Plans

    drawings, schematics and 3D printed parts files

    Sketchup 3D model with MSPhysics integration (Version 4):
    https://3dwarehouse.sketchup.com/mo...eec50f0a92/6DOF-Motion-Sim-by-FlyPT-Version-4
    update.jpg

    Dimensions of the hexapod (joints)
    Dimensions.png
    Full resolution at: https://www.flickr.com/photos/128465277@N06/31328973698/sizes/o/

    Files with the 3D printed parts for actuators using pot positioning:
    [​IMG]
    File: https://drive.google.com/open?id=1VDiJsNzJmQrOuzEaqN5gCoLyDfJcxLRb

    Files with the 3D printed parts for actuators using internal hall sensor positioning:
    SOON!

    Files with the 3D printed parts for the bottom and upper structure of the Stewart platform:
    SOON!

    Warning!
    Those files are made for my printer. They work for me and they have the right tolerances to fit the aluminium profiles and parts I use.
    They might not work for you. So use them at your own risk.
    They are printed in solid PETG. I think they are strong enough. I like to put the rig to a good stress, and until now they did not break.
    How long they will hold? Don't know!
    • Like Like x 2
    • Winner Winner x 1
    Last edited: Jan 24, 2020
  5. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Software
    Software used and code developed for this project

    INTERNAL HALL SENSOR POSITIONING VERSION WITH ESP32 DEVKIT


    Used software:
    SimTools (got the DIY license)
    LFS (demo version)
    Sketchup to draw all the printed parts (free)
    Cura to print 3D parts (free)
    MSPhysics plugin for Sketchup, to test angles and dimensions of rig (free)
    Arduino and needed libraries to program the ESP32 (free)


    Developed PC software
    To control the rig by calculating actuators positions to reach a specific pose.
    I recommend using my latest software:
    FlyPT Mover - https://www.xsimulator.net/community/threads/flypt-mover.13464/

    There's an older software you can also use, but that's a simpler version of Mover.
    FlyPT Hexapod Interface - https://www.xsimulator.net/communit...hexapod-interface-for-linear-actuators.12859/


    Developed controller software (ESP32)
    Installed in the ESP32, this code performs:
    -Actuators calibration (calculates range and middle extension)
    -Receives data (speed and/or position) from FlyPT Mover
    -Sends current actuators position to FlyPT Mover
    -Commands the motor controllers
    -Interprets the readings from the internal hall sensors to determine the actuator position

    You need to setup the Arduino IDE to program the ESP32, for that, use the following gides:

    To install the ESP32 in Arduino IDE, please follow this video:


    Extra info on the ESP32 - Please see the video, will help.


    Changing com port to use two ESP32 simultanouslly:



    Changing ID of ESP32 to have them start always in the same COM port
    Thanks to @Gabor Pittner for the tip (here):

    One of my biggest problem is that all ESP32 boards have the same hardware ID and COM ports are swapped sometimes in Windows.
    I've been struggling with this problem for months...:confused:
    Yesterday I found a solution how to change ESP32 board serial number to get a uniqe hardware ID. After it windows could reserve COM port to all uniqe ESP32 board.
    You need to change serial number using with CP21xx Device Customization Software:
    https://www.silabs.com/documents/public/example-code/AN721SW.zip
    Device Customization Software General Overview on page 24. :
    https://www.silabs.com/documents/public/application-notes/AN721.pdf
    Need to change serial number only!!! :cool:


    Code for ESP32 with external PID calculation
    Comming soon!


    Code for ESP32 with internal PID calculation, version: 11.3.is from 17-04-2020:
    Code:
    // ================================
    // DIY 6DOF Motion Sim by FlyPT
    // Version: 11.3.is from 17-04-2020
    // Internal PID calculation
    // ================================
    // SimTools2 ESP32 code to control up to 3 actuators
    // Actuators use brushless motors controlled by BLDC8015A drivers
    // The BLDC8015A receives speed and direction in separated pins
    // Position is obtained from internall hall sensors
    
    
    // ===========================
    // Serial output configuration
    // ===========================
    // Interface Type: Serial
    // ComPort: The one used by your ESP32
    // BitsPerSec: 921600 - If you want feedback on the serial monitor of the Arduino IDE, you have to change this to 500000 in the defines
    // Data Bits: 8
    // Parity: None
    // Stop Bits: 1
    // Bit Range: 15
    // Type: Binary
    // Interface string format: <255><255><Axis1a><Axis2a><Axis3a>
    /// Output Rate: Can be down to 1 ms
    
    
    // ===============
    // PID calculation
    // ===============
    // PID calculation is performed in the ESP32
    // In the PID calculation, we are calculating the speed that we want for the actuator.
    // We have a pretended position and a current position of the actuator and speed is calculated depending on those values.
    // Speed can be from -255 to 255, where negative speed is actuator going down and positive going up.
    // Values beyond this range are cropped.
    // Define each parameter PID in the defines, where:
    // P is the proportional part. The amount of P depends on  the distance
    
    
    // =======
    // Defines
    // =======
    #define P 256                   // Proportional
    #define I 0                     // Integral
    #define D 256                   // Diferential
    #define SERIAL_SPEED 921600     // Define to set up the serial speed comunication
    #define CALIBRATION_SPEED 50    // Speed used to search for minimum and maximum values (0 to 255) if calibration is to fast or to slow, adjust this value
    #define MAXIMUM_BIT_VALUE 32767 // Size of position values received (15 bits, above this, we use it to send other controls/requests besides position)
    #define UP HIGH                 // Define to set up direction as HIGH
    #define DOWN LOW                // Define to set down direction as LOW
    #define PWM_RESOLUTION 8        // 0 to 255
    #define PWM_FREQUENCY 1000      // The 8015A accepts up to 1 KHz
    #define LIMITS_PIN 0            // Array index of pin to capture position from pot wich defines the limits
    #define HALL_1_PIN 1            // Array index of pin used for hall 1 interrupt
    #define HALL_2_PIN 2            // Array index of pin used for hall 2 interrupt
    #define HALL_3_PIN 3            // Array index of pin used for hall 3 interrupt
    #define LAST_ANGLE 4            // Array index of last angle that was detected
    #define LAST_SUM 5              // Array index of last sum to know last direction it took
    #define DIRECTION_PIN 6         // Array index of Pin to se direction
    #define SPEED_PIN 7             // Array index of Pin to set speed with PWM
    #define PWM_CHANNEL 8           // Array index of channel used to generate PWM
    #define MINIMUM_POSITION 9      // Array index of minimum position
    #define MAXIMUM_POSITION 10     // Array index of maximum position
    #define CURRENT_POSITION 11     // Array index of current position
    #define PRETENDED_POSITION 12   // Array index of pretended position
    #define P_PID 13                // Array index of P from PID
    #define I_PID 14                // Array index of I from PID
    #define D_PID 15                // Array index of D from PID
    #define INTEGRATED_ERROR 16     // Array index of sum of differences between pretended and current position
    #define LAST_ERROR 17           // Array index of last difference between pretended and current position
    #define SPEED 18                // Array index of speed of motor rotation
    #define FOUND 19                // Array index of foud, used for in actuator calibration
    #define NUMBERACTUATORS 1       // Number of actuators (for one board, please don't change, current code does not support less than 3 actuators. To use less, just disconnect them at hardware level)
    
    
    // ======================
    // 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] = {
    //  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19
      {  4, 27, 26, 25,  5,  0, 19, 17,  0, -1, -1,  0,  0,  P,  I,  D,  0,  0,  0,  0 },  // Actuator 1
      { 36, 35, 34, 39,  5,  0, 18,  5,  1, -1, -1,  0,  0,  P,  I,  D,  0,  0,  0,  0 },  // Actuator 2
      { 15, 23, 22, 21,  5,  0, 32, 33,  2, -1, -1,  0,  0,  P,  I,  D,  0,  0,  0,  0 }   // Actuator 3
    };
    
    
    // =======================================
    // Variables for info received from serial
    // =======================================
    static int bufferPrevious = 0;      // To hold previous read fom serial command
    static int bufferCurrent = 0;       // To hold current read fom serial command
    static int bufferCount = 0;         // To hold current position in bufferCommand array
    static int bufferCommand[6] = {0};  // To hold received info from serial
    
    
    // ===========================================================================
    // Variables used in PID calculation to avoid repeated definitions in the loop
    // ===========================================================================
    static int limitValue = 1023;
    static int error = 0;
    static int p = 0;
    static int i = 0;
    static int d = 0;
    
    
    // ========================================================
    // Array used to calculate motor position from hall sensors
    // ========================================================
    // Maps to get increment off position from comparing previous hall sensor state with current state
    // In the table, vertical is the new value, horizontal is 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 stepUp[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 stepDown[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()
    {
      // Disable watchdog in both processors
      disableCore0WDT();
      disableCore1WDT();
    
      // Setup serial communication
      Serial.begin(SERIAL_SPEED, SERIAL_8N1);
    
      // Initialize
      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);
      }
    
      // 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
    
      // Initialize pin to light up onboard led and blink for 5 seconds before starting calibration
      pinMode(2, OUTPUT);
      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 minimum after calibration (for easy access to the rig)
      for (int n = 0; n < NUMBERACTUATORS; n++) actuator[n][PRETENDED_POSITION] = actuator[n][MINIMUM_POSITION];
    }
    
    
    // =========
    // Calibrate
    // =========
    void calibrate()
    {
      // Move all actuators to maximum
      // =============================
      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 big value 4095)
          // 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])) / 4 >= 4095)
          {
            ledcWrite(actuator[n][PWM_CHANNEL], 0);
            actuator[n][CURRENT_POSITION] = 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)
          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][MAXIMUM_POSITION] = -actuator[n][CURRENT_POSITION] / 2;
            actuator[n][MINIMUM_POSITION] = -actuator[n][MAXIMUM_POSITION];
            actuator[n][CURRENT_POSITION] = actuator[n][MINIMUM_POSITION];
            actuator[n][FOUND] = true;
            found++;
    
            // Send info to serial monitor for debug (use 500000 in serial speed to work with the Arduino IDE)
            Serial.println();
            Serial.print("Actuator ");
            Serial.print(n);
            Serial.print(":  Minimum=");
            Serial.print(actuator[n][MINIMUM_POSITION]);
            Serial.print("  Maximum=");
            Serial.println(actuator[n][MAXIMUM_POSITION]);
          }
        }
      }
    }
    
    
    // ======================
    // Update actuator action
    // ======================
    // Here we calculate the PID depending of desired vs current position and send respective signal to motor controller
    void updateActuatorSpeed(byte n)
    {
      // Calculate PID
      // =============
      error = actuator[n][PRETENDED_POSITION] - actuator[n][CURRENT_POSITION];
      actuator[n][INTEGRATED_ERROR] += error;
      p = actuator[n][P_PID] * error;
      i = actuator[n][I_PID] * constrain(actuator[n][INTEGRATED_ERROR], -255, 255);
      d = actuator[n][D_PID] * (error - actuator[n][LAST_ERROR]);
      actuator[n][LAST_ERROR] = error;
      actuator[n][SPEED] = FlyPT_Constrain((p + i + d) >> 8, -255, 255); // 2 4 8 16 32 64 128 256 Divide by 256
    
      // 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
      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 function (seems to be buggy)
    // ======================================================
    int FlyPT_Constrain(int value, int low, int high)
    {
      return (value < low) ? low : (value > high) ? high : value;
    }
    
    
    // ====================================
    // Update current position of actuators
    // ====================================
    void updateCurrentPosition(byte n)
    {
      int 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] = stepUp[angle][actuator[n][LAST_ANGLE]];
      else actuator[n][LAST_SUM] = stepDown[angle][actuator[n][LAST_ANGLE]];
      actuator[n][LAST_ANGLE] = angle;
      actuator[n][CURRENT_POSITION] += actuator[n][LAST_SUM];
    }
    
    
    // ===========================
    // Main loop running on core 1
    // ===========================
    // Here we get the simtools commands and make the actuator move
    void loop()
    {
      if (Serial.available())
      {
        bufferPrevious = bufferCurrent; // Store previous byte
        bufferCurrent = Serial.read(); // Get the new byte
        bufferCommand[bufferCount] = bufferCurrent; // Put the new byte in the array
        bufferCount++; // Change to next position in the array
        if(bufferCurrent==255 && bufferPrevious==255) bufferCount = 0; // Two 255 in sequence are the start of the position info
        if(bufferCount==NUMBERACTUATORS*2) // Having 6 bytes means we have the 3 positions and that we can update the pretended position
        {
          for (int n = 0; n < NUMBERACTUATORS; n++)
          {
            actuator[n][PRETENDED_POSITION] = map((bufferCommand[n*2] * 256) + bufferCommand[n*2+1], 0, MAXIMUM_BIT_VALUE, actuator[n][MINIMUM_POSITION], actuator[n][MAXIMUM_POSITION]);
          }
          bufferCount=0;
        }
      }
    
      // Update orders sent to motor driver
      for (int n = 0; n < NUMBERACTUATORS; n++) updateActuatorSpeed(n);
    }
    
    
    // ==============================================
    // Update positions thread loop running in core 0
    // ==============================================
    void TaskUpdatePositions(void * parameter)
    {
      while (true)
      {
        for (int n = 0; n < NUMBERACTUATORS; n++) updateCurrentPosition(n);
      }
    }
    
    • Like Like x 10
    Last edited: May 14, 2020
  6. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Software (continuation)
    Software used and code developed for this project

    POT POSITIONING VERSION WITH ARDUINO UNO
    Old, please use the esp32 code, way faster than this!

    I moved to the internal hall positioning solution, so this is no longer updated, but here for reference

    Used software:
    SimTools 2 (got the DIY license)
    LFS (demo version)
    Sketchup to draw all the printed parts (free)
    Cura 2.6 to print 3D parts (free)
    MSPhysics plugin for Sketchup, to test angles and dimensions of rig (free)
    Arduino to program the Arduino Uno (free)
    Google Chrome to browse this site...

    Developed software:
    Arduino Uno code that:
    -Calibrates actuators
    -Receives data from SimTools2
    -Implements PID to position actuators

    Update history:

    Version 8:
    -Corrected error in the PID calculation (affected D where error and last error where already equal, returning always zero for D)
    -Remove unnecessary calculus int he PID calculation
    -Commented I calculation, since it's not used and always returns zero

    Version 7:
    -Removed dead zone, no noticeable benefit. One less if.
    -Removed counts when searching for maximum minimum, no noticeable benefit since the analogue read repeats solves this problem.

    Version 6:
    -Add constrain to position value to avoid oscillation when actuator is close to limits and activates the switches (sudden change for min to 0 or max to 1024)
    -Uses two functions for speed reasons, this avoids more if's. One gets position cropped and the other one not cropped.

    Version 5:
    -Remove unused functions
    -Remove optional hack for speed, now it's always implemented

    Version 4:
    -More code optimisation, removing some loops
    -For that, functions where created that receive index of actuator
    -Remove direction from array, it's already stored on the signal of the speed

    Version 3:
    Code optimisation
    Renamed functions for better understanding

    Version 2:
    Add minimum and maximum calibration

    Latest version:
    Code:
    // ============================
    // DIY 6DOF Motion Sim by FlyPT
    // ============================
    // SimTools2 Arduino code to control 6DOF actuators
    // Actuators use brushless motors controlled by BLDC8015A drivers
    
    
    // ===================================
    // Update history - Current: Version 8
    // ===================================
    // Version 8:
    //  Corrected error in the PID calculation (affected D where error and last error where already equal, returning always zero for D)
    //  Remove unecessary calculus inthe PID calculation
    //  Commented I calculation, since it's not used and always returns zero
    // Version 7:
    //  Removed dead zone, no noticeable benefit. One less if.
    //  Removed counts when searching for maximum minimum, no noticeable benefit since the analogue read repeats solves this problem.
    // Version 6:
    //  Add constrain to position value to avoid oscillation when actuator is close to limits and activates the switches (sudden change for min to 0 or max to 1024)
    //  Uses two functions for speed reasons, this avoids more if's. One gets position cropped and the other one not cropped.
    // Version 5:
    //  Remove unused functions
    //  Remove optional hack for speed, now it's always implemented
    // Version 4:
    //  More code optimisation, removing some loops
    //  For that, functions where created that receive index of actuator
    //  Remove direction from array, it's already stored on the signal of the speed
    // Version 3:
    //  Code optimisation
    //  Renamed functions for better understanding
    // Version 2:
    //  Add minimum and maximum calibration
    
    
    // ==================================
    // SimTools2 configuration should be:
    // ==================================
    // Interface settings:
    // Interface Type: Serial
    // ComPort: The one used by your Arduino
    // BitsPerSec: 57600 (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: [1<Axis1a>][2<Axis2a>][3<Axis3a>][4<Axis4a>][5<Axis5a>][6<Axis6a>]
    // Output Rate: 10ms
    // Shutdown - Output: -
    // HW Stop: -
    
    
    // =======
    // Defines
    // =======
    #define NUMBER_ACTUATORS 6  // Define to set the number of actuators to use
    #define SERIAL_SPEED 57600 // Define to set up the serial speed comunication
    #define CALIBRATION_SPEED 60 // Speed used to search for minimum and maximum values
    #define ENDS_GAP 0 // Value added or subtracted to maximum and minimum positions when calibrating
    #define UP HIGH // Define to set up direction as HIGH
    #define DOWN LOW // Define to se down direction as LOW
    #define POSITION_PIN 0 // Holds index where this information is stored in the actuator array
    #define DIRECTION_PIN 1 // Holds index where this information is stored in the actuator array
    #define SPEED_PIN 2 // Holds index where this information is stored in the actuator array
    #define MINIMUM_POSITION 3 // Holds index where this information is stored in the actuator array
    #define MAXIMUM_POSITION 4 // Holds index where this information is stored in the actuator array
    #define CURRENT_POSITION 5 // Holds index where this information is stored in the actuator array
    #define PRETENDED_POSITION 6 // Holds index where this information is stored in the actuator array
    #define P_PID 7 // Holds index where this information is stored in the actuator array
    #define I_PID 8 // Holds index where this information is stored in the actuator array
    #define D_PID 9 // Holds index where this information is stored in the actuator array
    #define INTEGRATED_ERROR 10 // Holds index where this information is stored in the actuator array
    #define LAST_ERROR 11 // Holds index where this information is stored in the actuator array
    #define SPEED 12 // Holds index where this information is stored in the actuator array
    #define ANALOG_READ_REPEATS 16 // Number of times we get analog read to calculate a medium value (to reduce noise, maximum 32)
    // Defines for setting and clearing register bits (used for speed hack)
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif
    #define LOWBYTE(v)   ((unsigned char) (v))        //Read
    #define HIGHBYTE(v)  ((unsigned char) (((unsigned int) (v)) >> 8))
    #define BYTELOW(v)   (*(((unsigned char *) (&v) + 1)))      //Write
    #define BYTEHIGH(v)  (*((unsigned char *) (&v)))
    
    
    // ======================
    // Actuators information:
    // ======================
    // 0  - POSITION_PIN       - Pin to capture position from pot
    // 1  - DIRECTION_PIN      - Pin to se direction
    // 2  - SPEED_PIN          - Pin to set speed with PWM
    // 3  - MINIMUM_POSITION   - Minimum position
    // 4  - MAXIMUM_POSITION   - Maximum position
    // 5  - CURRENT_POSITION   - Current position
    // 6  - PRETENDED_POSITION - Pretended position
    // 7  - P_PID              - P from PID
    // 8  - I_PID              - I from PID
    // 9  - D_PID              - D from PID
    // 10 - INTEGRATED_ERROR   - Sum of differences between pretended and current position
    // 11 - LAST_ERROR         - Last difference between pretended and current position
    // 12 - SPEED              - Speed of motor rotation
    int actuator[6][13] = {
      {A0,  2,  3, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 1
      {A1,  4,  5, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 2
      {A2,  7,  6, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 3
      {A3,  8,  9, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 4
      {A4, 12, 10, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0},   //actuator 5
      {A5, 13, 11, 1023, 0, 0, 511, 100, 0, 100, 0, 0, 0}    //actuator 6
    };
    int buffer = 0; // To hold current read fom SimTools command
    int bufferCount = -1; // To hold position in bufferCommand array
    int bufferCommand[5] = {0}; // To hold command info from SimTools
    
    
    // ==============
    // Initialization
    // ==============
    void setup()
    {
      // Initialize serial comunication with SimTools2
      Serial.begin(SERIAL_SPEED);
      // Initialize pins
      for (byte n = 0; n < NUMBER_ACTUATORS; n++)
      {
        pinMode(actuator[n][POSITION_PIN], INPUT); // Pin to get position of actuator (0 to 1023)
        pinMode(actuator[n][DIRECTION_PIN], OUTPUT); // Pin to specify direction of rotation (0 or 1)
        pinMode(actuator[n][SPEED_PIN], OUTPUT); // Pin to specify speed (0 to 255)
      }
      // Speed up the arduino analogue read function setting analogue prescale to 16
      sbi(ADCSRA, ADPS2) ;
      cbi(ADCSRA, ADPS1) ;
      cbi(ADCSRA, ADPS0) ;
      // Calibrate actuators, searching for minimums and maximums
      calibrate();
    }
    
    
    // =======================================================================
    // Get actuator position (used in calibration to find minimum and maximum)
    // =======================================================================
    void getCurrentPosition(byte n)
    {
      // Make one reading
      actuator[n][CURRENT_POSITION] = analogRead(actuator[n][POSITION_PIN]);
      // Get determined number of readings and add them all
      for (byte m = 1; m < ANALOG_READ_REPEATS; m++) actuator[n][CURRENT_POSITION] += analogRead(actuator[n][POSITION_PIN]);
      // Divide to get medium value of the readings
      actuator[n][CURRENT_POSITION] /= ANALOG_READ_REPEATS;
    }
    
    
    // =======================================================================================
    // Get's actuators position constrained to maximum and minimum values found on calibration
    // =======================================================================================
    void getConstrainCurrentPosition(byte n)
    {
      // Make one reading
      actuator[n][CURRENT_POSITION] = analogRead(actuator[n][POSITION_PIN]);
      // Get determined number of readings and add them all
      for (byte m = 1; m < ANALOG_READ_REPEATS; m++) actuator[n][CURRENT_POSITION] += analogRead(actuator[n][POSITION_PIN]);
      // Divide to get medium value of the readings
      actuator[n][CURRENT_POSITION] /= ANALOG_READ_REPEATS;
      // Constrain position value to minimum/maximum range (this avoids oscilation when limit switches are activated)
      constrain(actuator[n][CURRENT_POSITION], actuator[n][MINIMUM_POSITION], actuator[n][MAXIMUM_POSITION]);
    }
    
    
    // =============================
    // Get information from SimTools
    // =============================
    void getSimToolsCommands()
    {
      // Look for start and finish of a command on the serial
      // When we have a command send it form analyze
      while (Serial.available())
      {
        if (bufferCount == -1)
        {
          buffer = Serial.read();
          if (buffer != '[') bufferCount = -1;
          else bufferCount = 0;
        }
        else
        {
          buffer = Serial.read();
          bufferCommand[bufferCount] = buffer;
          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
      switch (bufferCommand[0])
      {
        case '1':
          actuator[0][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[0][MINIMUM_POSITION], actuator[0][MAXIMUM_POSITION]);
          break;
        case '2':
          actuator[1][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[1][MINIMUM_POSITION], actuator[1][MAXIMUM_POSITION]);
          break;
        case '3':
          actuator[2][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[2][MINIMUM_POSITION], actuator[2][MAXIMUM_POSITION]);
          break;
        case '4':
          actuator[3][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[3][MINIMUM_POSITION], actuator[3][MAXIMUM_POSITION]);
          break;
        case '5':
          actuator[4][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[4][MINIMUM_POSITION], actuator[4][MAXIMUM_POSITION]);
          break;
        case '6':
          actuator[5][PRETENDED_POSITION] = map((bufferCommand[1] * 256) + bufferCommand[2], 0, 1023, actuator[5][MINIMUM_POSITION], actuator[5][MAXIMUM_POSITION]);
          break;
        default:
          return;
          break;
      }
    }
    
    
    // ==========================================================
    // Calculate PID for each actuator and update actuators array
    // ==========================================================
    void calculatePID(byte n)
    {
      float error = (float)actuator[n][PRETENDED_POSITION] - (float)actuator[n][CURRENT_POSITION];
      actuator[n][INTEGRATED_ERROR] += error;
      float p = ((float)actuator[n][P_PID] * error);
      //float i = ((float)actuator[n][I_PID] * constrain(actuator[n][INTEGRATED_ERROR], -100, 100)); // Uncomment to use I
      float d = ((float)actuator[n][D_PID] * (error - actuator[n][LAST_ERROR]));
      actuator[n][LAST_ERROR] = error;
      //actuator[n][SPEED] = constrain((p + i + d) / 200.0, -255, 255); // Uncomment to use I
      actuator[n][SPEED] = constrain((p + d) / 200.0, -255, 255); // Comment to use I
    }
    
    
    // ======================================================================
    // Picks values from actuators arrays and sends data to motor controllers
    // ======================================================================
    void updateController(byte n)
    {
      if (actuator[n][SPEED] > 0)
      {
        analogWrite(actuator[n][SPEED_PIN], actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], UP);
      }
      else
      {
        analogWrite(actuator[n][SPEED_PIN], -actuator[n][SPEED]);
        digitalWrite (actuator[n][DIRECTION_PIN], DOWN);
      }
    }
    
    
    // =========
    // Main loop
    // =========
    void loop()
    {
      // While to make main loop, avoiding some of the Arduino framework delays
      while (1 == 1)
      {
        getSimToolsCommands();
        for (byte n = 0; n < NUMBER_ACTUATORS; n++)
        {
          getConstrainCurrentPosition(n);
          calculatePID(n);
          updateController(n);
        }
      }
    }
    
    
    // ==================================================
    // Calibration, search for minimum and maximum values
    // ==================================================
    void calibrate()
    {
      getMinimums(); // We might be at the minimum already, better put all at minimum and then search maximum followed by minimum
      getMaximums();
      getMinimums();
    }
    
    
    // ====================================
    // Search for minimums in all actuators
    // ====================================
    void getMinimums()
    {
      byte found = 0;
      bool foundMinimum[6] = {false, false, false, false, false, false};
      for (byte n = 0; n < NUMBER_ACTUATORS; n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN], DOWN);
        analogWrite(actuator[n][SPEED_PIN], CALIBRATION_SPEED);
      }
      while (found < NUMBER_ACTUATORS)
      {
        for (byte n = 0; n < NUMBER_ACTUATORS; n++)
        {
          getCurrentPosition(n);
          if (!foundMinimum[n])
          {
            if (actuator[n][CURRENT_POSITION] > 0)
            {
              if (actuator[n][CURRENT_POSITION] < actuator[n][MINIMUM_POSITION]) actuator[n][MINIMUM_POSITION] = actuator[n][CURRENT_POSITION];
            }
            else
            {
              analogWrite(actuator[n][SPEED_PIN], 0);
              actuator[n][MINIMUM_POSITION] += ENDS_GAP;
              foundMinimum[n] = true;
              found++;
            }
          }
        }
      }
    }
    
    
    // ====================================
    // Search for maximums in all actuators
    // ====================================
    void getMaximums()
    {
      byte found = 0;
      bool foundMaximum[6] = {false, false, false, false, false, false};
      for (byte n = 0; n < NUMBER_ACTUATORS; n++)
      {
        digitalWrite (actuator[n][DIRECTION_PIN], UP);
        analogWrite(actuator[n][SPEED_PIN], CALIBRATION_SPEED);
      }
      while (found < NUMBER_ACTUATORS)
      {
        for (byte n = 0; n < NUMBER_ACTUATORS; n++)
        {
          getCurrentPosition(n);
          if (!foundMaximum[n])
          {
            if (actuator[n][CURRENT_POSITION] < 1023)
            {
              if (actuator[n][CURRENT_POSITION] > actuator[n][MAXIMUM_POSITION]) actuator[n][MAXIMUM_POSITION] = actuator[n][CURRENT_POSITION];
            }
            else
            {
              analogWrite(actuator[n][SPEED_PIN], 0);
              actuator[n][MAXIMUM_POSITION] -= ENDS_GAP;
              foundMaximum[n] = true;
              found++;
            }
          }
        }
      }
    }
    

    SimTools2 configuration should be:
    Interface settings:
    Interface Type: Serial
    ComPort: The one used by your Arduino
    BitsPerSec: 57600 (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: [1<Axis1a>][2<Axis2a>][3<Axis3a>][4<Axis4a>][5<Axis5a>][6<Axis6a>]
    Output Rate: 10ms
    Shutdown - Output: -
    HW Stop: -
    • Like Like x 2
    • Winner Winner x 1
    Last edited: Apr 17, 2020
  7. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Electronics
    Schematics used for this project

    IMPORTANT! Look here: https://www.xsimulator.net/communit...hless-diy-actuators.10799/page-38#post-217746

    INTERNALL HALL SENSOR POSITIONING VERSION WITH ESP32 DEVKIT
    Still using a similar solution to the pot positioning to detect minimum and maximum switches.
    The pot was replace by two resistances (10k):

    No Pot electronics v2.png
    (For full resolution: https://c2.staticflickr.com/2/1966/43113342910_bbfc75294e_o.png)

    So if minimum is pressed we get 0 in the ESP32.
    If maximum is pressed we get 4095 on the ESP32.
    None pressed we get around 2047.
    Now positioning is calculated from reading the hall sensors state.
    Lower resolution than the pot, but I think it's enough.

    For 6 actuators we need two ESP32 boards. They are connected the same way.
    Here's the current schematic in use:

    No Pot electronics 3 actuators.png
    (For full resolution: https://www.flickr.com/photos/128465277@N06/30098511787/sizes/o/)

    And the board I made with the ESP32 DevKit, plugs and level shifters:
    WP_20180727_18_25_46_Rich.jpg

    Mounted in the controllers "box":
    WP_20181112_17_02_57_Rich.jpg
    WP_20180925_17_35_19_Rich.jpg


    POT POSITIONING VERSION WITH ARDUINO UNO

    I moved to the internal hall positioning solution, so this is no longer updated, but here for reference.

    Pot and controller connection for one actuator:
    [​IMG]
    (For full resolution: https://c1.staticflickr.com/5/4389/36059736774_07d10f6b53_o.png)

    The idea is to control all the actuators with one Arduino Uno.
    I get position feedback with pots and two limit switches.
    When the maximum switch is pressed, the pot value is immediately 1023 on the Arduino.
    The same for the minimum, we get 0 when the switch is enabled
    While no limit switch is activated, we get the value from the pot.
    The motor controller needs speed and direction information.
    Speed is fed with PWM and direction is on/off.
    So with this config I can fit everything in one Arduino Uno:
    [​IMG]
    (For full resolution: https://c1.staticflickr.com/5/4362/36059748064_4c644dc11d_o.jpg)
    • Like Like x 1
    Last edited: Jun 1, 2021
  8. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    I already have two actuators made.
    Built the first one as a prototype, looking for design problems.
    Built the second one, just to find new problems o_O
    So this is the current design (will update the above posts with link to Sketchup files):

    Some Sketchup drawings (those are still WIP):
    [​IMG]

    [​IMG]

    The parts to be printed in solid PETG:
    [​IMG]


    1: Join the screw with the slider and aluminium tube:
    [​IMG]

    [​IMG]

    [​IMG]

    [​IMG]

    Please ignore that bad print, it was already replaced.
    Aluminium tube is hold tight by two screws, one in each side of the PETG "slider".
    The two visible holes will get two screws to mount a GT2 belt


    2: Mount motor support

    Open threads in the aluminium profile:
    [​IMG]

    Slide the motor mount in place and put the screws:
    [​IMG]

    Mount the other side:
    [​IMG]



    3: Mount ball screw, slider and aluminium tube in the motor mount:
    [​IMG]
    • Like Like x 7
    Last edited: Sep 26, 2018
  9. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    3: Mount ball screw, slider and aluminium tube in the motor mount:
    [​IMG]

    3: Top end actuator plate.
    Here we have the wheel for the GT2 belt and an Ingus bearing:

    [​IMG]

    [​IMG]

    [​IMG]

    [​IMG]

    [​IMG]

    We need to open some M5 threads on the aluminium:
    [​IMG]
    Only made 4 or 5 by hand, next ones with WD40 and electric drill...

    Testing the mounting:
    [​IMG]
    • Like Like x 5
    Last edited: Oct 2, 2018
  10. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    4: Mounting wires and limit switches:

    Solder wires, pass them between aluminium profile and motor mount.
    [​IMG]
    (note: microswitch should be placed before top end or we can't mount the housings for the switches)

    [​IMG]
    Motor already mounted

    Insert micro switches in 3D printed housings and tight them to the aluminium profile:
    [​IMG]

    [​IMG]
    Here we can see the slider touching the switch

    Updated list of parts, software and added a video on the posts at the start of this thread.
    • Like Like x 6
  11. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    5: Make cables that connect to the control box.
    In my idea, I want to build a box with all the power supply and controllers.
    When we have kids, just the actuator is already pretty dangerous, so electrics should be really safe and hidden, at least for cosmetic reasons.

    So, since I could not get the needed cables, I made my owns.
    Joined 6 packs of wires:
    [​IMG]

    With 11 wires, 3 for limit switches and pot 3 to power the motor and the rest for the hall sensor:
    [​IMG]

    Joined them at one end so I could put them inside a sleeve:
    [​IMG]

    [​IMG]

    And the result:
    [​IMG]
    • Like Like x 6
  12. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    6: Bottom plate
    This holds the pot and the UJoint. Must be strong!

    Printed two equal parts.
    They make a sandwich to hold the pot.
    All wires go inside and the go out in one cable, the one built in the above post:
    [​IMG]

    There's also a part to mount this on the aluminium profile and hold the UJoint:
    [​IMG]

    [​IMG]

    Mounting everything:
    [​IMG]

    [​IMG]

    [​IMG]
    This one with the belt already in place.
    • Like Like x 16
  13. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Some updates to the Arduino code.
    Updated the post about software in the start of the thread with the new code.

    Version 5:

    -Remove unused functions
    -Remove optional hack for speed, now it's always implemented

    Version 4:
    -More code optimisation, removing some loops
    -For that, functions where created that receive index of actuator
    -Remove direction from array, it's already stored on the signal of the speed

    Version 3:
    Code optimisation
    Renamed functions for better understanding

    Version 2:
    Add minimum and maximum calibration
    • Like Like x 2
    • Agree Agree x 1
    • Winner Winner x 1
  14. matthew loomis

    matthew loomis Member

    Joined:
    May 18, 2017
    Messages:
    94
    Location:
    rochester,ny
    Balance:
    603Coins
    Ratings:
    +82 / 0 / -0
    My Motion Simulator:
    2DOF, DC motor, Arduino
    Pretty impressive. You made your own code for the uno. Pretty cool. I was trying to avoid that by using rc components. Much better solution. How do you handle the pid tuning? I've seen some arduino code for self tuning pid but don't have the skills to paste that into an existing sketch.
    Glad someone got brushless working.
    • Like Like x 2
  15. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    PID tuning is manual, we can change the values in the array for each motor.
    Really curious about that code with self tuning. Can you give me a link?
    I'm still having vibrations in stand still, around 10º rotation off the motor to each side.
    I'm uploading a video right now with 2 actuators and this code. Still some time to go...

    EDIT:
    Found the Arduino library for PID control and tuning, will have a look.
    But it must be fast for 6 motors.
    I still have to tune mine manually.
    Last edited: Aug 31, 2017
  16. matthew loomis

    matthew loomis Member

    Joined:
    May 18, 2017
    Messages:
    94
    Location:
    rochester,ny
    Balance:
    603Coins
    Ratings:
    +82 / 0 / -0
    My Motion Simulator:
    2DOF, DC motor, Arduino

    GitHub - jackw01/arduino-pid-autotuner: Automated PID tuning using Ziegler-Nichols/relay method on Arduino and compatible boards
    https://github.com/jackw01/arduino-pid-autotuner
    • Like Like x 1
  17. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    1,868
    Location:
    Portugal
    Balance:
    14,203Coins
    Ratings:
    +2,181 / 16 / -0
    My Motion Simulator:
    6DOF
    Uploaded a new video.
    We can see two actuators working with LFS (like a 2DOF).
    At the start, the Arduino code is searching for the minimum and maximum positions, performing a basic calibration.

    • Like Like x 7
    • Friendly Friendly x 1
  18. SeatTime

    SeatTime Well-Known Member

    Joined:
    Dec 27, 2013
    Messages:
    2,574
    Occupation:
    Retired
    Location:
    Brisbane Australia
    Balance:
    28,370Coins
    Ratings:
    +2,844 / 38 / -0
    My Motion Simulator:
    AC motor, Motion platform
    Nice :thumbs, What sort of load can they lift?
  19. baykah

    baykah Active Member

    Joined:
    Sep 1, 2016
    Messages:
    137
    Location:
    Toulouse - France
    Balance:
    414Coins
    Ratings:
    +132 / 0 / -0
    My Motion Simulator:
    6DOF
    Nice !

    Carefull with the potentiometers, I had bad experience with cheap pots ... and the way you mounted them all the force of the belt tension goes to the pot shaft ...

    It would be better to have the pot axe free from stress ..
    • Agree Agree x 2
  20. SimPotato

    SimPotato Member

    Joined:
    May 17, 2017
    Messages:
    47
    Occupation:
    Designer
    Location:
    Gardiner, NY USA
    Balance:
    479Coins
    Ratings:
    +53 / 0 / -0
    My Motion Simulator:
    3DOF, DC motor, Arduino
    Beautiful craftsmanship.. I like the 3 d printed parts on aluminum profiles together.
    Are the plastic parts solid density?
    Do you use special filament?
    What is the actuator max speed ?

    Thanks