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 DIY 2DOF wiper motors

Discussion in 'DIY Motion Simulator Projects' started by jfbegot, Sep 5, 2018.

  1. jfbegot

    jfbegot New Member

    Joined:
    Jan 14, 2018
    Messages:
    5
    Location:
    Sete, France
    Balance:
    198Coins
    Ratings:
    +3 / 0 / -0
    My Motion Simulator:
    2DOF, Arduino
    Frame: welded steel tube
    Motors: 12V wiper motors
    Electronic: VNH2SP30 (monster moto clone) + Arduino
    Power supply: 12V 30A (less should be OK)


    The motors: car wiper motors.
    I bought used Peugeot 206 motor and frame because I the axis of these motors is special (grooved and conical end). I I re-used some of the linkage.
    [​IMG]
    In a first time I thought that one motor per axis should be enough but finally I did put two motors per axis.

    Caution!: the negative is earthed in these motors. You have to open, cut the wire connected to the frame and output a wire connected to the negative.
    Caution (again): these are two speed motors choose the good one. I don't know wich I used but one works better than the other.

    The electronics:
    I used one VNH2SP30 driver per motor drived by an arduino UNO (any type of Arduino should work).

    The code:
    I wrote my own PID control and comunication protocol.

    ... To be continued

    Attached Files:

  2. jfbegot

    jfbegot New Member

    Joined:
    Jan 14, 2018
    Messages:
    5
    Location:
    Sete, France
    Balance:
    198Coins
    Ratings:
    +3 / 0 / -0
    My Motion Simulator:
    2DOF, Arduino
    Some pictures and the code:

    P_20180901_195343.jpg P_20180902_120843.jpg P_20180902_120915.jpg P_20180902_120952.jpg P_20180902_121037.jpg

    Now the code (Without any guarantee that it is going to work)

    Code:
    /*****************************
     *MotorPidJef4
     *Drive two DC motors at a specific position detected by two potentiometers
     *The position is controlled by PID
     *I has been designed to work with two VNH2SP30 drivers but should work with others drivers with ENABLE and PWM inputs.
     *Serial communication: 115200, 8bits, No parity
     *Format:
     *Only ASCII characters:  *<command>numerical value<not numerical character>
     *<command>: can be:
     *  L or R for left axis or right axis
     *  P to set max PWM value (to be used during tuning but you can do what you want)
     *numerical value: 0 to 255
     *<not numerical character>: any character other than 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
     *Examples:
     *R214;
     *L23.
     *R45<cr>
     *L85x
     *P200,
     *
     *Wiring: see below
     *Tuning: if you want to change KP, KI, KD be aware that calculations are made in int (to avoid slow float math calculations).
     *So: overflows can occur with high values of KP, KI, KD
     ****************************/
    
    //Pin definitions
    //potentiometers
    #define POTLEFTPIN A4
    #define POTRIGHTPIN A5
    //Motor 1 pin definitions
    #define INA1PIN 7
    #define INB1PIN 8
    #define PWM1PIN 5
    //Motor 1 pin definitions
    #define INA2PIN 4
    #define INB2PIN 9
    #define PWM2PIN 6
    //H-bridge drive definitions
    #define BRAKEVCC 0
    #define FWD  1 //beware it's depending on your hardware wiring
    #define BWD  2 //beware it's depending on your hardware wiring
    #define STOP BRAKEVCC
    #define BRAKEGND 3
    
    //PID Parameters
    #define KP 4 //proportional gain
    #define KI 0 //unuszed here
    #define KD 4 //unused here
    #define PWMMAXDEFAULT 255 //max pwm at startup
    #define PIDMILLISDELAY 20 //delay (ms) between PID executions
    #define PWMMINI 15 //minimum value of PWM
    
    //Potentiometers range
    //to be adjusted to limit the travel (0 - 1023)
    #define RPOTMINI 180
    #define RPOTMAXI 770
    #define LPOTMINI 190
    #define LPOTMAXI 790
    
    //faster to write
    #define MOTORLEFT 0
    #define MOTORRIGHT 1
    
    //global variables ================
    
    //PID
    int pwmMax = PWMMAXDEFAULT;
    unsigned long pidMillisSchedule;
    
    //communication
    char frameBuffer[16] = "R128.";
    unsigned char parserFlag = 0;
    unsigned char frameIndex = 0;
    int frameValue = 0;
    unsigned char newFrame = 0;
    char frameCommand = ' ';
    
    //target Positions
    int targetLeft = 512; //middle position 0-1023
    int targetRight = 512; //middle position 0-1023
    
    //potentiometers acquisition and averaging
    unsigned int potLvalue, potRvalue;
    unsigned int potLbuf[16], potRbuf[16];
    uint8_t potIndex = 0;
    
    
    // Setup ================
    void setup() {
      Serial.begin(115200);
    
      // Pins init
      pinMode(INA1PIN, OUTPUT);
      pinMode(INB1PIN, OUTPUT);
      pinMode(PWM1PIN, OUTPUT);
      pinMode(INA2PIN, OUTPUT);
      pinMode(INB2PIN, OUTPUT);
      pinMode(PWM2PIN, OUTPUT);
    
      // H-bridge braked
      digitalWrite(INA1PIN, LOW);
      digitalWrite(INB1PIN, LOW);
      digitalWrite(INA2PIN, LOW);
      digitalWrite(INB2PIN, LOW);
    
      // targets init
      frameValue = 128;
      targetRight = map(frameValue, 0, 255, RPOTMINI, RPOTMAXI);
      targetLeft = map(frameValue, 0, 255, LPOTMINI, LPOTMAXI);
    
      //pid init
      pidMillisSchedule = millis() + PIDMILLISDELAY;
    }// setup() ***************
    
    // Main Loop ==============
    void loop() {
      serialParser(); //serial protocol management
      if (newFrame) {
        newFrame = false; // acknowledge frame
    
        switch (frameCommand) {
          case 'R':
            targetRight = map(frameValue, 0, 255, RPOTMINI, RPOTMAXI);
            break;
    
          case 'L':
            targetLeft = map(frameValue, 0, 255, LPOTMINI, LPOTMAXI);
            break;
    
          case 'P':
            pwmMax = frameValue;
            break;
        }//switch frameCommand
      }//if newFrame
    
      // read and average positions
      potRbuf[potIndex] = analogRead(POTRIGHTPIN);//add samples
      potLbuf[potIndex] = analogRead(POTLEFTPIN);
    
      if (potIndex < 15) potIndex++; else potIndex = 0; //index management
    
      potLvalue = 0; potRvalue = 0; //averaging
      for (uint8_t i = 0; i < 16; i++) {
        potLvalue += potLbuf[i];
        potRvalue += potRbuf[i];
    
      }//for i
      potLvalue = potLvalue >> 4;
      potRvalue = potRvalue >> 4;
    
      if (pidMillisSchedule <= millis()) { //PID executed at this rate
        pidMillisSchedule += PIDMILLISDELAY; //schedule next execution
        motorPID(MOTORRIGHT, potRvalue, targetRight);
        motorPID(MOTORLEFT, potLvalue, targetLeft);
      }//if millis
    } // loop() ************
    
    //===========================
    // Serial parser: to be called at a periodic rate
    void serialParser() {
      char rxChar; //char received
    
      if (Serial.available()) {//we work only if char received
        rxChar = Serial.read();
        if (Serial.available() > 20)  {
          frameIndex = 0;
          Serial.flush(); //frame overflow
        }//if serial.available>20
        else {
          switch (parserFlag) {
            case 0: //waiting for start char
              if ((rxChar == 'R') || (rxChar == 'L') || (rxChar == 'P')) {
                frameBuffer[0] = rxChar;
                frameIndex = 1; //one char in buffer
                parserFlag = 1; //start char received
              }// if rxChar
              break;
    
            case 1:
              if (isDigit(rxChar)) {
                frameBuffer[frameIndex] = rxChar; //save received char
                frameIndex ++;
                if (frameIndex > 4) {
                  parserFlag = 0;
                  frameIndex = 0; //frame too long
                }
              } else  {
    
                if (frameIndex == 1) {
                  parserFlag = 0;
                  frameIndex = 0; //empty frame: we give up
                } else {
                  frameValue = 0;
                  for (int i = 1; i < frameIndex; i++) frameValue = frameValue * 10 + (frameBuffer[i] - 48);
                  frameCommand = frameBuffer[0];
                  parserFlag = 0;
                  frameIndex = 0; //ready for new frame
                  newFrame = 1;
                }//else frameIndex
              }//else isdigit
              break;
    
          }// switch parserFlag
        }//else serial.available>20
      }// if Serial.available
    }// serialParser() ****************
    
    //=========================
    // Motors PID management: to be called at a fixed rate (~20ms)
    void motorPID(int numMot, int actualPos, int targetPos) {
      int pidError;
      int deriveError;
      static int oldError[2];
      static long integralError[2][8], integralMoy;
      static uint8_t integralIndex = 0;
    
      int pidOutput;
      unsigned char dir;
      int pwm;
    
      pidError = targetPos - actualPos;
      deriveError = pidError - oldError[numMot];
      oldError[numMot] = pidError;
      integralError[numMot][integralIndex] = pidError;
      if (integralIndex < 7) integralIndex++; else integralIndex = 0;
      integralMoy = 0;
      for (uint8_t i = 0; i < 8; i++) integralMoy += integralError[numMot][i];
    
      pidOutput = (pidError * KP) + (deriveError * KD) + (integralMoy / 4 * KI);
      if (pidOutput > 0) {//error positive
        dir = BWD;
        pwm = pidOutput;
      }// if pidOutput>0
      else {//error negative
        dir = FWD;
        pwm = -pidOutput;
      }// if pidOutput<0
    
      motorDrive(numMot, dir, pwm);
    
    }//fin motorPID() **************
    
    //=========================
    //Drive the motor through H-bridge
    void motorDrive(uint8_t motor, uint8_t direct, int pwm) {
      if (motor == 0) {//motor 1
        switch (direct) {
          case BRAKEVCC: //electromagnetic brake : brake VCC
            digitalWrite(INA1PIN, HIGH);
            digitalWrite(INB1PIN, HIGH);
            break;
          case BRAKEGND: //Brake Ground (free wheel)
            digitalWrite(INA1PIN, LOW);
            digitalWrite(INB1PIN, LOW);
            break;
          case FWD:
            digitalWrite(INA1PIN, HIGH);
            digitalWrite(INB1PIN, LOW);
            break;
          case BWD:
            digitalWrite(INA1PIN, LOW);
            digitalWrite(INB1PIN, HIGH);
            break;
        }//switch direct
        //clip the PWM
        if (pwm < PWMMINI) pwm = 0; //minimum PWM
        else if (pwm > pwmMax)  pwm = pwmMax;
        analogWrite(PWM1PIN, pwm);
      }//if motor
    
      else {// motor 2
        switch (direct) {
          case 0: //electromagnetic brake : brake VCC
            digitalWrite(INA2PIN, HIGH);
            digitalWrite(INB2PIN, HIGH);
            break;
          case 3: //Brake Ground (free wheel)
            digitalWrite(INA2PIN, LOW);
            digitalWrite(INB2PIN, LOW);
            break;
          case 1: // forward : beware it's depending on your hardware wiring
            digitalWrite(INA2PIN, HIGH);
            digitalWrite(INB2PIN, LOW);
            break;
          case 2: // Reverse : beware it's depending on your hardware wiring
            digitalWrite(INA2PIN, LOW);
            digitalWrite(INB2PIN, HIGH);
            break;
        }//switch direct
        //clip the PWM
        if (pwm < PWMMINI) pwm = 0;
        else if (pwm > pwmMax) pwm = pwmMax;
        analogWrite(PWM2PIN, pwm);
      }//else motor
    }// motorDrive() ******************
    
  3. jfbegot

    jfbegot New Member

    Joined:
    Jan 14, 2018
    Messages:
    5
    Location:
    Sete, France
    Balance:
    198Coins
    Ratings:
    +3 / 0 / -0
    My Motion Simulator:
    2DOF, Arduino
    A loooong video here.
    I will post a better one soon...
  4. jfbegot

    jfbegot New Member

    Joined:
    Jan 14, 2018
    Messages:
    5
    Location:
    Sete, France
    Balance:
    198Coins
    Ratings:
    +3 / 0 / -0
    My Motion Simulator:
    2DOF, Arduino

    Formula One at Azure Coast in Project Cars by Rookie driver.
    • Like Like x 2
  5. FoxHound.92

    FoxHound.92 2DOF seat mover Gold Contributor

    Joined:
    Jan 11, 2018
    Messages:
    69
    Location:
    Germany
    Balance:
    38Coins
    Ratings:
    +30 / 0 / -0
    My Motion Simulator:
    2DOF, DC motor, Arduino
    Wow that is some heavy movement right there! :eek:
    If there were no seatbealt he would be thrown around easily!
  6. jfbegot

    jfbegot New Member

    Joined:
    Jan 14, 2018
    Messages:
    5
    Location:
    Sete, France
    Balance:
    198Coins
    Ratings:
    +3 / 0 / -0
    My Motion Simulator:
    2DOF, Arduino
    Yes, the guy on the seat hits the curbs, guardrails and other cars. It is more realistic when you can stay on the track.
    • Like Like x 1
  7. Arta Yasa

    Arta Yasa Member

    Joined:
    Jan 27, 2021
    Messages:
    153
    Balance:
    752Coins
    Ratings:
    +14 / 0 / -0
    My Motion Simulator:
    2DOF
    what is the function of the rubber in each corner of your dof sir?