Please note project is still in alpha stage and to be treated as a work in progress.
Having freshly purchased a 12 year old Vauxhall Astra H, I found parking still a challenge, especially proximity of reversing into narrow spaces. The car is not equipped with any form of sensors or reverse cameras at purchase.
To create my own sensor relay for the older car similar to how modern cars solve this problem.
Specification:
Must accurately indicate distance rear of car is from nearby surfaces at multiple points
Must alert driver to collision situation via a warning system.
Must be install-able and removable without requirement of automotive garage specific tools.
Must rely on more than simple directional audio indicators.
Main Linux Platform.
Used due to inexpensive system.
Small and can be fitted and concealed into car.
Complex enough to run HDMI display natively.
Low power draw.
Micro-controller Platform.
Plenty of IO pins for running multiple sensors.
Low power draw.
Inexpensive & small
Has plenty of pre-built libraries and documentation.
Ultrasonic Rangefinder Sensor.
Durable and waterproof.
Inexpensive and compact.
Used for this role in many similar products.
Long cable and low profile to make installation easier.
Boards able to be stack able.
Visual Display.
Small but clear to see at a glance being 720p.
Easy to mount with its own bracket. So can be secured on dashboard.
HDMI Input/AV input, Can incorporate both Raspberry pi and additional Camera inputs directly.
12V power draw.
Ultrasonic range finders are sensor modules which emit a sound pulse at timed intervals and record the response. By using the time difference between emitting and receiving the module can predict distances from the module to the nearest semi solid structure. A more detailed write up can be found here.
By constantly polling multiple Ultrasonic range finders in parallel, positional data to nearby structures can be recorded in real time.
Code Components:
Sensor IO.
Serial Reader.
GUI.
#define trigPin 10
#define echoPin 13
#define trigPin1 2
#define echoPin1 3
#define DELAY 1000
void setup() {
Serial.begin(9600);
pinMode(trigPin,OUTPUT);
pinMode(echoPin,INPUT);
pinMode(trigPin1,OUTPUT);
pinMode(echoPin1,INPUT);
} // setting up 2 sensors trigger and echo pins,
void loop() {
float duration,duration1, distance,distance1;
digitalWrite(trigPin,LOW);
digitalWrite(trigPin1,LOW);
delayMicroseconds(2);
// main loop starting conditions
//Left Sensor sending out pulse and recording the response time
digitalWrite(trigPin,HIGH);
delayMicroseconds(10);
digitalWrite(trigPin,LOW);
duration = pulseIn(echoPin,HIGH);
distance = (duration/2) * 0.0344; // converting the response into a distance.
//Right Sensor sending out pulse and recording the response time
digitalWrite(trigPin1,HIGH);
delayMicroseconds(10);
digitalWrite(trigPin1,LOW);
duration1 = pulseIn(echoPin1,HIGH);
distance1 = (duration1 /2) * 0.0344;
// Serial Output
Serial.print("Left:");
Serial.print(distance1);
Serial.print(" ");
Serial.print("Right");
Serial.println(distance);
delay(DELAY); // hard coded delay, can be adjusted for tuning the polling rate of sensors
}
Currently setup for dual sensors as in two for the front of the car left and right side. Hence defining two trigger and two echo pins.
Running Serial baud rate 9600 as rate of transmission of the serial is not critical as i am conveying qualitative information rather than quantitative information.
Sensors are pulled down by default, so i set the trigger pins LOW (0) as a resting state.
Main loop starts by setting the write pin high and held high for 10 micro seconds and then switched to low. The transmitter sends the signal out for that 10 micro seconds. The echo pin is acting as a receiver during this and will record via the Pulse In when the echo pin sets high (receives signal).
The duration is calculated from the time difference between start of transmission and start of retrieval. As included in the theory section, the distance is then calculated.
The Output of this code is just through a serial USB connection which prints as individual words to the raspberry pi.
Then the hard coded delay which can alter how often sensors update.
This is old code, not performing to the Embedded C standards and using Arduino's IDE, please see more recent work for better conforming c code.
Full code found on the github page, including unit tests, key snippets have been selected and displayed.
Highlight #1: Configured Serial port named Ardunio for connect. Connected to method readSerial when readyRead() signal is received.
if(arduino_is_available){
arduino->setPortName(arduino_port_name);
arduino->open(QSerialPort::ReadOnly);
arduino->setBaudRate(QSerialPort::Baud9600);
arduino->setDataBits(QSerialPort::Data8);
arduino->setParity(QSerialPort::NoParity);
arduino->setStopBits(QSerialPort::OneStop);
arduino->setFlowControl(QSerialPort::NoFlowControl);
QObject::connect(arduino, SIGNAL(readyRead()),this,SLOT(readSerial()));
}
Highlight #2: readSerial() method reads the serial input into an array then adapts the data using strings and stringlists into a bufferSplit[1] a string holding a full line written across the serial port.
/**
* @brief Serial reading from ardunio, reading as array into a buffer then split as to indicate where new inputs start.
* buffer is split with
*/
void MainWindow::readSerial(){
QByteArray serialIn = arduino->readAll(); //this saves current info from the arduino into ByteArray
serialBuffer += QString::fromStdString(serialIn.toStdString()); //String is easiest way to lump this data.
bufferSplit = serialBuffer.split("\n"); //Stringlist provides best supported array of string information
if(bufferSplit.length()>=3){ //Adds a future component requirement to make sure buffer is filled.
MainWindow::updatetext(bufferSplit[1]);
MainWindow::updateProgressbar(bufferSplit[1]);
serialBuffer=""; //resetting serial buffer.
}
}
Highlight 3#: Nested Conditional statements with limits set the colour for each bar and the value. Allowing GUI to visibly change on the levels of danger on collision.
//imposing limits which categories into different coloured bars. Alerting for danger.
if(rightSensorint>10&&rightSensorint<500){
if(leftSensorint>10&&leftSensorint<500){
ui->progressBar->setValue(leftSensorint);
ui->progressBar_2->setValue(rightSensorint);
if(leftSensorint<30){
ui->progressBar->setStyleSheet(danger);}
else if (leftSensorint<50&&leftSensorint>=30) {
ui->progressBar->setStyleSheet(semidanger);
}
else if (leftSensorint>50) {
ui->progressBar->setStyleSheet(safe);
}
if(rightSensorint<30){
ui->progressBar_2->setStyleSheet(danger);}
else if (rightSensorint<50&&leftSensorint>=30) {
ui->progressBar_2->setStyleSheet(semidanger);
}
else if (rightSensorint>50) {
ui->progressBar_2->setStyleSheet(safe);
}
}
}
User Interface design is as simple as possible Two progress bars taking up the majority of the space with a small divide.
This is to maximise visuals on small application screens. While minimising attention requirement for a driver. The GUI is designed to expand to fit the allocated window size. So future scope could embed this as part of a larger dashboard or just to populate on different resolution screens.
QT Designer UI Design.
UI as Displayed on screen.
A poor quality video of the application in action as i move objects to different ranges from the sensors.
GUI Bars were created using QT's Progress Bar and slider Libraries. Set via Pointers allowing adaptive customisation later if i want to change sensitivities on the fly or limits. I also set the bars to invert as to me a bar filling up as i get closer to an object felt counter intuitive.
//Setting up the GUI Progress bars appearance and ranges
ui->progressBar->setRange(0, 100);
ui->progressBar_2->setRange(0,100);
ui->progressBar->setInvertedAppearance(0);
ui->progressBar_2->setInvertedAppearance(0);
ui->progressBar->setStyleSheet(error);
ui->progressBar_2->setStyleSheet(error);
ui->progressBar->setTextVisible(1);
ui->progressBar_2->setTextVisible(1)
GUI Bars were created using QT's Progress Bar and slider Libraries. The code isn't complex or detailed, mostly being generated via QT UI Design tool after I visually created the layout. Click to expand if interested.
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>428</width>
<height>344</height>
</rect>
</property>
<property name="font">
<font>
<family>Serif</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<widget class="QWidget" name="centralWidget">
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Serif</family>
<pointsize>20</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="textDirection">
<enum>QProgressBar::BottomToTop</enum>
</property>
<property name="format">
<string>%p CM</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>38</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QProgressBar" name="progressBar_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Serif</family>
<pointsize>20</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="value">
<number>24</number>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="format">
<string>%p CM</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
Improvements:
Sensor IO: Change the hard coded delay to accept a potentiometer value, which allows the poll rate to be altered more easily when moving.
Sensor IO: Switch the Dual sensors to an Array of sensors for better coverage of rear and sides of car, allowing for a 360 degree field of view, GUI wise i would represent this as a model with the car in the centre and rays of different coloured light to indicate distances to nearest object.
Switch from Arduino IDE to a more mature IDE, Which allows for more normal embedded C programming environment. With better adoption of Embedded C standards.
Build the sensor system into a larger system using the CAN bus thus allowing for integration into automotive grade Linux operating system or similar.
Include a dash camera feed with front and back cameras, then overlay the coloured progress bars over the correct feed, indicating accurate measurements whilst a visual on what the obstruction is.
Challenges:
Main difficulties experienced were related to buffering the serial data coming in and then refining and processing into just the raw sensor data. Achieving this in real time without causing inflated memory overhead.
Learning how to check for individual vendor boards on the Serial reader application so that the systems fault tolerance was able to establish a connection to correct serial port.