2DOF with 4 x DC worm gear motors

Running simulator build projects.
Please use the image gallery for your pictures, a short tutorial can be found here.
The first image in the first post will be shown in the project gallery.

2DOF with 4 x DC worm gear motors

Postby jolebaleze » Sun 25. Oct 2015, 11:59

Another 2DOF seat with DC motors (wiper style).
The motors:
3848
These motors cost 40€ for a complementary pair (left/right):
Voltage Range: 12V / 24V
No load current: 0.66A / 0.68A
Torque: more than 11KG / CM
Speed: 12V -> 22 turn/min, 24V -> 50 turn/min
Weight: 900 g

I want to use two of these motors per axis so i can get 22kg/cm torque @ 12V and a rotational speed of 22t/min.

The mechabical parts:
3847
I plan tu use the 4 rubber shock absorbers to build the same function as a Cardan joint.

The electronics and power supply:
3846
I hope the PC PSU will be strong enough to drive the motors. If not, I will use two PSU.
The DC motor drivers will be the MonsterMoto shield cooled with ventirad.

The wheel/pedals:
3850
The Logitech G920 for Xbox/PC.
It is the more (very) expensive part of the project but it is the more easy to obtain(when you have the money).

The game will be Projetc Cars:
3849
I first tried X-sim with Grid. I was able to output the datas with USO.
Un fortunately, it seems that Grid doesn't support Force Feedback Wheel.

Now:
I try to make X-Sim to extract data from Project Cars but I can't get anything until *
All my stuff is useless as long as I can't make Project Cars/X-Sim work.
I hope that using the Stage 2 version will help.

To be continued...
jolebaleze
 
Posts: 5
Images: 11
Joined: Sun 13. Sep 2015, 17:11
Location: Montpellier - France
Has thanked: 1 time
Been thanked: 0 time

Re: 2DOF with 4 x DC worm gear motors

Postby jolebaleze » Sun 1. Nov 2015, 15:39

I am back with some news.
Project Cars is now working with X-sim.
The problem was that "shared memory" was not enabled in Project Cars.

I built a prototype to test some solutions:
3857

The back:
3853

The motors fitting:
3854

Power Supply and electronics:
3855

Arduino and MotoMonster:
3858

The electronics and the code:
I started from RacingMat solution Arduino + MotoMonster (thanks to RacingMat).
I experienced a lot of oscillations probably due to the poor quality of my proto.
I managed to write a new code for the Arduino based upon the racingMat protocol but using a PID algorithm (only proportional finally).

Here is the code:
Code: Select all
/*======================================
 MotorPidJef v0
 Arduino code for DC motor PID motion based on MonsterMoto shield form Sparkfun https://www.sparkfun.com/products/10182
 Can be used for another H-bridge driver: change pin and H-bridge drive definitions
 No additional library needed
 Only proportional PID (it seems to be enough for X-sim purpose)
 Inspired by RacingMat work  http://www.x-sim.de/forum/posting.php?mode=edit&f=37&t=943&p=8481
 Same fixed length field protocol as defined by racingMat exept:
 - only 2 digit capitals hexadecimals numbers supported
 - 'P' (max pwm setting) command added
 Format : <command><number>
 with
  - <command> : R = right motor target, L = Left motor target, P = max pwm setting
  - <number> : a two digits hexadecimal number in capitals.
  Examples:
  R45
  RF3
  L05
  L7F
  PFF
  P90
 ======================================*/
 

//Pin definitions
//potentiometers
#define POTLEFTPIN A4
#define POTRIGHTTPIN 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 FW  1 //beware it's depending on your hardware wiring
#define RV  2 //beware it's depending on your hardware wiring
#define STOP BRAKEVCC
#define BRAKEGND 3

//PID Parameters
#define KP 10 //proportional gain
#define KI 1 //unuszed here
#define KD 1 //unused here
#define DEADZONE 10 //error gap where PID is not active
#define PWMMAXDEFAULT 255 //max pwm at startup
#define PIDMILLISDELAY 20 //delay (ms) between PID executions

//Range of potentiometers
//to be set depending of your hardware
#define POTMINI 210
#define POTMAXI 810

//abritrary choice: change as you need
#define MOTORLEFT 0
#define MOTORRIGHT 1

//global variables ================

//PID
int pwmMax = PWMMAXDEFAULT;
unsigned long pidMillisSchedule;

//communication
char frameBuffer[4] = "R80";
unsigned char frameIndex = 0;

//target Positions
int targetLeft = 512; //middle position 0-1023
int targetRight = 512; //middle position 0-1023


// 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);

  //pid init
  pidMillisSchedule = millis() + PIDMILLISDELAY;
}// setup() ***************

// Main Loop ==============
void loop() {
  int potLvalue, potRvalue;

  serialParser(); //serial protocol management

  // read positions
  potRvalue = analogRead(POTRIGHTTPIN);
  potLvalue = analogRead(POTLEFTPIN);

  if (pidMillisSchedule <= millis()) { //PID executed at this rate
    pidMillisSchedule += PIDMILLISDELAY; //schedule next execution
    motorMotion(MOTORRIGHT, potRvalue, targetRight);
    motorMotion(MOTORLEFT, potLvalue, targetLeft);
  }//if millis
} // loop() ************

//===========================
// Serial parser: to call at a rate higher than the rx rate
void serialParser() {
  char rxChar; //char received

  if (Serial.available()) {//work only if char received
    rxChar = Serial.read();
    if (frameIndex > 2)  {
      frameIndex = 0;
      Serial.flush(); //frame overflow
    }//if frameindex>1
    else {
      switch (frameIndex) {
        case 0: //waiting for start char
          if ((rxChar == 'R') || (rxChar == 'L') || (rxChar == 'P')) {
            frameBuffer[0] = rxChar;
            frameIndex = 1; //start char received
          }// if rxChar
          break;

        case 1:
          frameBuffer[1] = rxChar; //save received char
          frameIndex = 2;
          break;

        case 2:
          frameBuffer[2] = rxChar; //save received char
          if (checkHex(&frameBuffer[1])) { //check if a valid hex byte is received
            switch (frameBuffer[0]) {
              case 'R':
                targetRight = normalizeTarget(hexToByte(&frameBuffer[1]));
                break;

              case 'L':
                targetLeft = normalizeTarget(hexToByte(&frameBuffer[1]));
                break;

              case 'P':
                pwmMax = hexToByte(&frameBuffer[1]);
                break;
            }// switch frameBuffer
          }// if checkHex
          frameIndex = 0; //frame has been handled: ready for new frame
          break;
      }// switch frameIndex
    }// else frameIndex>1
  }// if Serial.available
}// serialParser() ****************

//=========================
// Motors PID management: to call at a fixed rate
void motorMotion(int numMot, int actualPos, int targetPos) {
  int pidError;
  int pidOutput;
  int pwm;
  int brakingDistance = 30;

  // security concern : targetPos has to be within the mechanically authorized range
  targetPos = constrain(targetPos, POTMINI + brakingDistance, POTMAXI - brakingDistance);

  pidError = targetPos - actualPos;

  if (abs(pidError) < DEADZONE) motorDrive(numMot, FW, BRAKEVCC); //small error: no movement
  else {
    pidOutput = pidError * KP;
    if (pidOutput > 0) {//error positive
      if (pidOutput > pwmMax) pwm = 255;
      else pwm = pidOutput;
      if (actualPos < POTMAXI) motorDrive(numMot, RV, pwm);
      else motorDrive(numMot, FW, pwm);
    }//fin if pidOutput>0
    else if (pidOutput < 0) {//error negative
      if (pidOutput < -255) pwm = 255;
      else pwm = -pidOutput;
      if (actualPos > POTMINI) motorDrive(numMot, FW, pwm);
      else motorDrive(numMot, RV, pwm);
    }//fin if pidOutput<0
    else pwm = 0; //error=0: should not happen beacause DEADZONE
  }//fin DEADZONE
}//fin motorMotion() **************

//=========================
//Drive the motor through H-bridge
void motorDrive(uint8_t motor, uint8_t direct, uint8_t 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 FW: // forward : beware it's depending on your hardware wiring
        digitalWrite(INA1PIN, HIGH);
        digitalWrite(INB1PIN, LOW);
        break;
      case RV: // Reverse : beware it's depending on your hardware wiring
        digitalWrite(INA1PIN, LOW);
        digitalWrite(INB1PIN, HIGH);
        break;
    }// switch direct
    if (pwm <= pwmMax) analogWrite(PWM1PIN, pwm);
    else analogWrite(PWM1PIN, pwmMax);
  }//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
    if (pwm <= pwmMax) analogWrite(PWM2PIN, pwm);
    else analogWrite(PWM2PIN, pwmMax);
  }//else motor
}// motorDrive() ******************

//=================================
// map the range from Xsim (0 <-> 255) to the mechanically authorized range (POTMINI <-> POTMAXI)
int normalizeTarget(byte x) {
  int result;
  result = map(x, 0, 255, POTMINI, POTMAXI);
  return result;
} // normalizeTarget() **************

//=======================
//convert a number in ASCII coded HEX on 2 digits into a byte
//check if valid before!
byte hexToByte(char x[2]) {
  byte result;
  if (x[0] > 47 && x[0] < 58 ) x[0] = x[0] - 48;
  if (x[0] > 64 && x[0] < 71 ) x[0] = (x[0] - 65) + 10;
  if (x[1] > 47 && x[1] < 58 ) x[1] = x[1] - 48;
  if (x[1] > 64 && x[1] < 71 ) x[1] = (x[1] - 65) + 10;
  result = x[0] * 16 + x[1];
  return result;
}// hex<ToByte() ******************

//======================
//check if a two digit ASCII coded HEX number is valid
byte checkHex(char x[2]) {
  byte result;
  if ((((x[0] > 47) && (x[0] < 58)) || ((x[0] > 64) && (x[0] < 71))) && (((x[1] < 58) && (x[1] > 47)) || ((x[1] > 64) && (x[1] < 71)))) result = true;
  else result = false;
  return result;
}// checkHex() ************

Proportional driving works fine on my prototype.
It is a choice to not use functions from stdio for parsing: the frame format is simple and the code is (I hope) more portable, compact and efficient this way.

X-sim configuration:

I am still working on "Math setup" to obtain a realistic motion.
I tested it with my sons on the seat because I thought that I was too heavy for the proto...
... and it was true.
After some repairs I will continue the development on the proto (without sitting on the seat this time).
I will build a strong and clean metallic frame but I want to obtain better results with the proto.

To be continued...
jolebaleze
 
Posts: 5
Images: 11
Joined: Sun 13. Sep 2015, 17:11
Location: Montpellier - France
Has thanked: 1 time
Been thanked: 0 time

Re: 2DOF with 4 x DC worm gear motors

Postby RacingMat » Sun 1. Nov 2015, 21:53

Yes, weak motor won't behave correctly under load with a PID requiring speed and torque:
you're right thinking the oscillations come from your motors.

And do you have quality CERMET potentiometers?
Do you have a film of the oscillations?

Glad you found a way to reduce them :)

I'll look at the modified code to see your work!
Mat
2 DOF playseat : arduino, motomonster, 12V truck wiper motors
http://www.x-sim.de/forum/viewtopic.php?f=37&t=943
User avatar
RacingMat
X-Sim Stage 2 edition
 
Posts: 456
Images: 147
Joined: Wed 20. Feb 2013, 21:30
Location: Marseille - FRANCE
Has thanked: 4 times
Been thanked: 130 times

Re: 2DOF with 4 x DC worm gear motors

Postby jolebaleze » Mon 2. Nov 2015, 21:58

matRacing You're right: my motors are weak but I did put two of them per axis. Each motor delivers a torque of 10Nm: so 20Nm per axis. It is a good value don't you think?
There are no more oscillations * I can't say if the cause is the enhancements I made on the mechanics or the use of my powerfull code ;). I made both at the same time.
My potentiometers are standard carbon ones. I will use Cermet on the final version. Anyway I think (I hope) they are working right until now because they are new ones.
I think I have to work on the excessive angular course of the seat too. There are two ways to do that: the cinematics and the math setup.
I will reduce the length of the arms of the motors and check the math setup then.
I'll be back (soon or later).
jolebaleze
 
Posts: 5
Images: 11
Joined: Sun 13. Sep 2015, 17:11
Location: Montpellier - France
Has thanked: 1 time
Been thanked: 0 time

Re: 2DOF with 4 x DC worm gear motors

Postby liqiang » Sun 7. Aug 2016, 05:25

设计还没有完成吗朋友!
liqiang
 
Posts: 2
Joined: Sat 6. Aug 2016, 15:22
Has thanked: 0 time
Been thanked: 0 time


Return to Motion simulator Projects in progress

Who is online

Users browsing this forum: No registered users and 4 guests