TIMOTHY MWALA
Published © GPL3+

Autonomous Driving Car V4.X

A 1:10 RC AI car where a neural network does the driving and an embedded controller guarantees it stops.

IntermediateFull instructions provided5 hours153
Autonomous Driving Car V4.X

Things used in this project

Hardware components

Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
×1
XMC1100 firmware driving the shield
×1
Flash Memory Card, MicroSD Card
Flash Memory Card, MicroSD Card
×1
DC-SHIELD BTN9970LV
Infineon DC-SHIELD BTN9970LV
×1
RC steering servo
×1
Camera Module V2
Raspberry Pi Camera Module V2
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

Donkey_xmc_code

C/C++
The code contains some modifications from the previous versions, //watchdog: stop the car if DonkeyCar stops sending commands (Pi crash, cable off). Adjust them based on your throttles and steering calibration
#include "btn99x0_motor_control.hpp"
using namespace btn99x0;


// servo pin
int servo = 8;

// variables for servo control
int pwm;
int Servo_pos = 0;
int Servo_pos_aktuell;

// serial communication
String inString = "";
int neg;

//variables for motor control 
int speedvalue = 0;
int speedvalue_aktuell;
int speedflag = false;


void servoMove(int servo, int Servo_pos){
  pwm = (Servo_pos) + 1500;
  digitalWrite(servo, HIGH);
  delayMicroseconds(pwm);
  digitalWrite(servo, LOW);
}

//i2c communication
int on1;
int off1;

int on0;
int off0;

int r;        //register
int x;        //value

int received;

//watchdog: stop the car if DonkeyCar stops sending commands (Pi crash, cable off)
const unsigned long CMD_TIMEOUT_MS = 500;  //DonkeyCar writes every 50 ms at 20 Hz
const int SERVO_CENTRE_POS = -100;         //calibrated centre pulse 500, minus 600
volatile unsigned long lastCmdMs = 0;      //set in receiveEvent, read in loop
bool watchdogTripped = false;

#include <Wire.h>
DCShield shield;
MotorControl btn_motor_control(shield);

void setup() {
//communications
  Wire.begin(64);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent);  // register event
  Wire.onRequest(requestEvent);  // register event
  Serial.begin(9600);          // start serial for output
  delay(10);
//servo
 pinMode(servo, OUTPUT);
 servoMove(servo, Servo_pos);

//motor   

  btn_motor_control.begin();
  Serial.println("Initialising speed");
  btn_motor_control.set_speed(speedvalue);
  Serial.println("Starting motor");
  Serial.println("Setup Done");
  


}

void loop() {
    
  if(received == 0) {
    Serial.println("fail!");
  }
    
  //watchdog: no complete command for CMD_TIMEOUT_MS -> brake motor, centre steering
  noInterrupts();
  unsigned long sinceCmdMs = millis() - lastCmdMs;
  interrupts();
  if(sinceCmdMs > CMD_TIMEOUT_MS){
    if(!watchdogTripped){
      Serial.println("watchdog: no commands, motor stopped");
      watchdogTripped = true;
    }
    btn_motor_control.set_speed(0);
    servoMove(servo, SERVO_CENTRE_POS);
    delay(20);
    return;
  }
  watchdogTripped = false;

  //servo control
    if(abs(Servo_pos_aktuell - Servo_pos)<200){ //fail save
    servoMove(servo, Servo_pos);   
    }
  Servo_pos_aktuell = Servo_pos; 

  //motor control
  if(abs(speedvalue_aktuell - speedvalue)<200){ //fail save
    btn_motor_control.set_speed(speedvalue*0.4);
    }
  speedvalue_aktuell = speedvalue; 
  
  delay(20);
 
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write(0x01); // respond with 0x01 as expected by master
  Serial.println("0x01");
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {  //checking how many bytes are received
  if(howMany == 2){               //expecting a pair of I2C register and value byte
  received = true;
  }
  else{
    received = false;
  }

     
// read
  while (1 < Wire.available()) { // receive a pair of one register byte and value byte
      r = Wire.read();           // receive register byte
  }
      x = Wire.read();           // receive Value byte as an integer

//process two Speed bytes | two Steer bytes
  switch (r) {  
    case 8:                     //first speed byte
      off0 = x;                 //map read value to first speed byte 
      speedflag = true;         //flag for first speed byte
      break;
    case 9:                     //second speed byte
      if (speedflag == true) {  //only if first speed byte was received
      off0 = off0+(x<<8);       //create two byte speed value
      speedvalue = off0-255;    //calculate offset to match with setup of donkeycar
      lastCmdMs = millis();     //feed the watchdog
      speedflag = false;        //reset flag
      }   
      break;
    case 12:                    //first steering byte
      off1 = x;                 //map read value to first steering byte
      break;
    case 13:                    //second steering byte
      off1 = off1+(x<<8);       //create two byte steering value
      Servo_pos = off1-600;     //calculate offset to match with setup of donkeycar
      lastCmdMs = millis();     //feed the watchdog
      break;   
 }
}

Credits

TIMOTHY MWALA
33 projects • 22 followers
I am an Embedded engineer who likes prototyping/ open to collaboration

Comments