How to determine object(car) turning right or left

Hi,

I’m working on a project that it’s similiar cars adaptive headlight control system, as you may know some of cars turning on fog lights according to turning direction when you turn wheel left or right, my project basis on the same principal.

My car doesn’t have this option and i want to add it, the problem is i don’t know is that any sensor determining turn,i thought i can do it with using magnets + Hall Effect sensor(i can attach magnets to wheel shaft and position sensor to shaft’s box so when shaft turns left or right it could trigger the sensor but it needs two sensors and hard mechanical handwork) but looking for much more sensitive way.

I thought maybe i should use MPU6050 angle sensor (i can mount to the wheel shaft but i need to use very long cables to avoid any harm when turning wheel).

Is there a smooth way other than above methods to detect turning direction of the wheel?

You can use the MPU6050 fixed to your car. It detects accelerations and usually you get an acceleration if you turn the wheel (if you're not on ice) as the car will make a turn. So if you drive straight you have only accelerations in the direction of your car's direct axis (accelerate the car or brake). If you get an acceleration in another direction you make a turn. OK, you need some filtering because of road roughness but that's probably the way where need least car modifications.

What kind of car? There was a thread a good while back about hacking a steering wheel encoder disk. Possibly you could access that, if it exists on your vehicle, via the CANbus.

dougp:
What kind of car? There was a thread a good while back about hacking a steering wheel encoder disk. Possibly you could access that, if it exists on your vehicle, via the CANbus.

unfortunately my car doesn't have wheel encoder (2004 fabia mk1) that's why i'm looking for a workarround.

pylon:
You can use the MPU6050 fixed to your car. It detects accelerations and usually you get an acceleration if you turn the wheel (if you're not on ice) as the car will make a turn. So if you drive straight you have only accelerations in the direction of your car's direct axis (accelerate the car or brake). If you get an acceleration in another direction you make a turn. OK, you need some filtering because of road roughness but that's probably the way where need least car modifications.

it's a good approach,i can do filtering no problem i'll give it a try.

ok now i’m trying my MPU6050 with arduino nano, found a good article here Arduino and MPU6050 Accelerometer and Gyroscope Tutorial and following steps.

My wiring is like this : MU6050 - Image on Pasteboard

and using this code :

/*
   Arduino and MPU6050 Accelerometer and Gyroscope Sensor Tutorial
   by Dejan, https://howtomechatronics.com
*/
#include <Wire.h>
const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;
void setup() {
  Serial.begin(19200);
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  /*
  // Configure Accelerometer Sensitivity - Full Scale Range (default +/- 2g)
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro Sensitivity - Full Scale Range (default +/- 250deg/s)
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)
  Wire.endTransmission(true);
  delay(20);
  */
  // Call this function if you need to get the IMU error values for your module
  calculate_IMU_error();
  delay(20);
}
void loop() {
  // === Read acceleromter data === //
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  //For a range of +-2g, we need to divide the raw values by 16384, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z-axis value
  // Calculating Roll and Pitch from the accelerometer data
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - 0.58; // AccErrorX ~(0.58) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 1.58; // AccErrorY ~(-1.58)
  // === Read gyroscope data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // For a 250deg/s range we have to divide first the raw value by 131.0, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
  // Correct the outputs with the calculated error values
  GyroX = GyroX + 0.56; // GyroErrorX ~(-0.56)
  GyroY = GyroY - 2; // GyroErrorY ~(2)
  GyroZ = GyroZ + 0.79; // GyroErrorZ ~ (-0.8)
  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
  gyroAngleY = gyroAngleY + GyroY * elapsedTime;
  yaw =  yaw + GyroZ * elapsedTime;
  // Complementary filter - combine acceleromter and gyro angle values
  roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
  pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;
  
  // Print the values on the serial monitor
  Serial.print(roll);
  Serial.print("/");
  Serial.print(pitch);
  Serial.print("/");
  Serial.println(yaw);
}
void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gyro data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
}

at first 30/40 seconds it works well but after that values are starting to increase even if i not moving the sensor (it’s just standing on my desk)

am i doing something wrong or the sensor is broken?

here are the screen shots :


sheshman:
it's a good approach,i can do filtering no problem i'll give it a try.

Yes, its a good place to start, just you will need to detect a CHANGE in acceleration, so parking on an incline will not give a false reading.
The 6050 can also give you gyro output, so that may be a variable to monitor, gyro about the vertical axis.
Tom.. :slight_smile:

Hi,
OPs circuit.

Tom... :slight_smile:

Hi,
Is there a reason you are not using the MPU6050 library?

google arduino mpu6050 library

Tom.. :slight_smile:

TomGeorge:
Hi,
Is there a reason you are not using the MPU6050 library?

google arduino mpu6050 library

Tom… :slight_smile:

damn me if i know why :slight_smile: i’ve changed my code to this (example code in the MPU6050 folder) and now it reads just fine :

// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class
// 10/7/2011 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
//      2013-05-08 - added multiple output formats
//                 - added seamless Fastwire support
//      2011-10-07 - initial release

/* ============================================
I2Cdev device library code is placed under the MIT license
Copyright (c) 2011 Jeff Rowberg

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================
*/

// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "MPU6050.h"

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 accelgyro;
//MPU6050 accelgyro(0x69); // <-- use for AD0 high

int16_t ax, ay, az;
int16_t gx, gy, gz;



// uncomment "OUTPUT_READABLE_ACCELGYRO" if you want to see a tab-separated
// list of the accel X/Y/Z and then gyro X/Y/Z values in decimal. Easy to read,
// not so easy to parse, and slow(er) over UART.
#define OUTPUT_READABLE_ACCELGYRO

// uncomment "OUTPUT_BINARY_ACCELGYRO" to send all 6 axes of data as 16-bit
// binary, one right after the other. This is very fast (as fast as possible
// without compression or data loss), and easy to parse, but impossible to read
// for a human.
//#define OUTPUT_BINARY_ACCELGYRO


#define LED_PIN 13
bool blinkState = false;

void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    // initialize serial communication
    // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
    // it's really up to you depending on your project)
    Serial.begin(38400);

    // initialize device
    Serial.println("Initializing I2C devices...");
    accelgyro.initialize();

    // verify connection
    Serial.println("Testing device connections...");
    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

    // use the code below to change accel/gyro offset values
    /*
    Serial.println("Updating internal sensor offsets...");
    // -76	-2359	1688	0	0	0
    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
    Serial.print("\n");
    accelgyro.setXGyroOffset(220);
    accelgyro.setYGyroOffset(76);
    accelgyro.setZGyroOffset(-85);
    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
    Serial.print("\n");
    */

    // configure Arduino LED for
    pinMode(LED_PIN, OUTPUT);
}

void loop() {
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

    // these methods (and a few others) are also available
    //accelgyro.getAcceleration(&ax, &ay, &az);
    //accelgyro.getRotation(&gx, &gy, &gz);

    #ifdef OUTPUT_READABLE_ACCELGYRO
        // display tab-separated accel/gyro x/y/z values
        Serial.print("a/g:\t");
        Serial.print(ax); Serial.print("\t");
        Serial.print(ay); Serial.print("\t");
        Serial.print(az); Serial.print("\t");
        Serial.print(gx); Serial.print("\t");
        Serial.print(gy); Serial.print("\t");
        Serial.println(gz);
    #endif

    #ifdef OUTPUT_BINARY_ACCELGYRO
        Serial.write((uint8_t)(ax >> 8)); Serial.write((uint8_t)(ax & 0xFF));
        Serial.write((uint8_t)(ay >> 8)); Serial.write((uint8_t)(ay & 0xFF));
        Serial.write((uint8_t)(az >> 8)); Serial.write((uint8_t)(az & 0xFF));
        Serial.write((uint8_t)(gx >> 8)); Serial.write((uint8_t)(gx & 0xFF));
        Serial.write((uint8_t)(gy >> 8)); Serial.write((uint8_t)(gy & 0xFF));
        Serial.write((uint8_t)(gz >> 8)); Serial.write((uint8_t)(gz & 0xFF));
    #endif

    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
}

Thanks Tom.

by the way, we can detect speed of a shaft with hall effect sensor, but can i detect turning direction of a shaft with hall effect sensor ?

sheshman:
by the way, we can detect speed of a shaft with hall effect sensor, but can i detect turning direction of a shaft with hall effect sensor ?

With just one hall effect sensor, unfortunately no.
You need two sensors in what is called quadrature configuration.
A quad encoder detecting steering wheel movement would do the trick.
Tom… :slight_smile:

Hi,

I'd go for something like a magnet attached to the track rod with two sensors (reed contacts possibly) an inch or two either side but attched to the cars bodywork.

That way small movements of the steering wheel to correct the cars position on the road shouldn't signal a turn but a definite 'turn' would be picked up.

Peter

I would think the accelerometer would be a nightmare to get every scenario working. Personally (assuming the car doesn’t have a steering angle sensor already) I would attach an encoder to the steering shaft via a pulley system and you would always know when the wheel was moving.

TomGeorge:
With just one hall effect sensor, unfortunately no.
You need two sensors in what is called quadrature configuration.
A quad encoder detecting steering wheel movement would do the trick.
Tom… :slight_smile:

first of all thanks for your interest and time, i’m still trying to find best way in my head, correct me if i’m wrong if i use two hall effect sensors and attach a magnet to wheel shaft, when i turn the wheel to left then magnet will pass from two sensors and if i code arduino like this;

if (1. sensor triggered first and then second triggered)
{
do things when i turn left
}
else if (2.sensor triggered first and 1. sensor triggered)
{
do things when i turn right
}
else
{
oh my goodness :)
}

ok now we find out turning left/right, but what about centering the wheel again,with above logic when started to turn regarded light switched on and when we completed the turn we need to center the wheel again (well my wife doesn’t do that all the time but you know how it is :slight_smile: ) so i guess i need 3rd hall effect sensor to find out when wheel is centered again, the basic coding should be like this :

if (1. sensor triggered first and then second triggered)
{
do things when i turn left
}
else if (2.sensor triggered first and 1. sensor triggered)
{
do things when i turn right
}
else if (3. sensor triggering>=5seconds)
{
shut down the lights
}
else
{
oh my goodness :)
}

as far as i understand attached bad boy is the right component to do such things, but ther is no way to mount to wheel (well you can but you need to give up from your airbag), there is no way to mount to shaft directly you need to invent a mechanical part(like strings or chain to turn the encoder’s stick) to make it work it means so much unnecessary work (we have a saying in my country, why you holding your left ear with your right hand, so it fits this situation).

I’ve studied cars with angle sensor most of them using industriel type electronical rotary encoders which is very expensive for me.

I’m sure there is a better way but need to think more :slight_smile:

dustin02rsx:
I would think the accelerometer would be a nightmare to get every scenario working. Personally (assuming the car doesn't have a steering angle sensor already) I would attach an encoder to the steering shaft via a pulley system and you would always know when the wheel was moving.

well, accelerometer is a good approach in my case, because all i need is A-X segment, i don't need AY-AZ or G-X G-Y, the problem with MPU6050 is you need to start a turn to activate fog light, i mean if you start to turn wheel while you stopping then system will not work, you need to accelerate then MPU6050 can detect your direction.

If you need to use other segments (AY-AX-AZ-GY-GX) then you are right, it could be a nightmare,because there are so many possibilites and will need so many adjustments.