/Realzeitsysteme_SS17

Semester project for realtime systems

Primary LanguageC

Overview

Hardware

We've used the provided hardware consisting of a Raspberry Pi 3, 4x Infrared sensors, a RFID reader, a front-directed ultrasonic sensor, piezo speaker and a hardware killswitch.

Picture of the final robot:

Software

Control/Dataflow of the software: (larger)

General

The software itself is divided into several logical modules. Mainly we have the "input" side, the processing side and the executing side. To connect the input part with the logic part we have chosen a simple system based on shared data structures. The data is of course protected with a rw-lock to prevent multi-threaded race-conditions. Furthermore, the structure includes an event timestamp to prevent reading of outdated data.

The logic thread itself reacts based on this input and the logicmode that is set. The logicmode signals which sub-logic module is to run. We made states to test each sensor and for the actual competition (path, search). This mode is exposed for more convenient usability over a command-line option.

The engine thread is controlled with a shared variable engineCtrl by the logic thread, based on that, the motor movement is changed to the desired mode. This could be a simple full-speed or a PWM-based action. The required kill-switch functionality was implemented over a thread that is constantly polling the GPIO pin connected to the button. If the button is pressed the thread sends a SIGINT signal to his own thread to shutdown the program. We tried to implement a kernel module for the killswitch but did not made it in time.

We have chosen C as the main programming language of this project due to our confidence in C and our lack of knowledge in writing C++ or Rust. Also we could use and extend our good old helper.c utility that we wrote during BSYS course.

Sensor-specific

Ultrasonic

The ultrasonic sensor was the most problematic in the system. It had two major shortcomings: the small detection angle and the strange firmware. We read in some forums that there are manufacturing faults that cause occasional breakdowns of the sensor. This leads to problems because sometimes, the sensor doesn't return anything and sometimes, it detects always an obstacle. The former could be solved by introducing timestamps to the measurements. We heard from other groups that another soultion would be to use the Linux poll function instead of actively polling.

RFID

Due to his complicated binary protocol we had to use a external library that did the low-level work for us. The problem was to get this C++ libary into a C project. So our solution was to write a custom bridging header and the usage of g++ for compiling the library and the linking of the program.

Infrared

Solid, simple sensors. Straight 10/10.

Kernel Configuration

The Kernel is based on the Linux kernel 4.4.9. The kernel was patched with the "RT Preempt" patch from https://www.kernel.org/pub/linux/kernel/projects/rt/4.4/patch-4.4.9-rt17.patch.gz. In order to document the current status of the kernel please find the debug-outputs below.

The full kernel features can be read here https://lwn.net/Articles/146861/. We don't use any of this features at our project. The reason we use the patch is that the most of deamons are disabled.

Output of command service --status-all:

Enable Name of deamon Why the deamon is used
[ + ] alsa-utils used for our easter egg the "piep" at driving backward
[ + ] avahi-daemon need for network
[ + ] bluetooth could be disabled
[ - ] bootlogs
[ - ] bootmisc.sh
[ - ] checkfs.sh
[ - ] checkroot-bootclean.sh
[ - ] checkroot.sh
[ + ] console-setup need for console input
[ + ] cron never used, can be disabled
[ + ] dbus never used, can be disabled
[ + ] dhcpcd never used, can be disabled
[ + ] dphys-swapfile used at interupts to swap files
[ + ] fake-hwclock never used, can be disabled
[ - ] hostname.sh
[ - ] hwclock.sh
[ + ] kbd need for keyboard input
[ + ] keyboard-setup need for keyboard input
[ - ] killprocs
[ + ] kmod used to load modules
[ - ] motd
[ - ] mountall-bootclean.sh
[ - ] mountall.sh
[ - ] mountdevsubfs.sh
[ - ] mountkernfs.sh
[ - ] mountnfs-bootclean.sh
[ - ] mountnfs.sh
[ + ] networking needed for network
[ - ] nfs-common
[ + ] ntp need for syncronisation with network
[ - ] plymouth
[ - ] plymouth-log
[ + ] procps has a bunch of small useful utilities that give information about processes
[ + ] raspi-config used to configurate the raspberry. e.g. to enable the whole space of the sd.card
[ + ] rc.local never used, can be disabled
[ - ] rmnologin
[ - ] rpcbind
[ - ] rsync
[ + ] rsyslog used for getting system logs
[ - ] sendsigs
[ + ] ssh need to start with ssh the programm
[ - ] sudo
[ + ] triggerhappy never used, can be disabled
[ + ] udev menage I/O for devices
[ + ] udev-finish never used, can be disabled
[ - ] umountfs
[ - ] umountnfs.sh
[ - ] umountroot
[ + ] urandom need to get random numbers
[ - ] x11-common

As we can see not all unused daemons, like bluetooth or cron are disabled.

The output of cat /proc/config.txt:

arm_freq=1000 dtparam=spi=on dtparam=audio=on device_tree_param=spi=on dtoverlay=spi-bcm2708 dtoverlay=i2c-rtc dtoverlay=w1-gpio isolcpus=1,2,3

Output of top command:

top - 17:44:01 up 1:51, 2 users, load average: 0.16, 0.20, 0.18 Tasks: 121 total, 1 running, 120 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 1.3 sy, 0.0 ni, 98.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem: 945516 total, 88984 used, 856532 free, 14108 buffers KiB Swap: 102396 total, 0 used, 102396 free. 40288 cached Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1926 pi 20 0 5108 2432 2088 R 11.8 0.3 0:00.04 top
1 root 20 0 22852 3864 2736 S 0.0 0.4 0:02.98 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.02 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:+
7 root 20 0 0 0 0 S 0.0 0.0 0:00.11 rcu_sched
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
9 root rt 0 0 0 0 S 0.0 0.0 0:00.01 migration/0
10 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 lru-add-dr+
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/0
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/1
13 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/1
14 root 20 0 0 0 0 S 0.0 0.0 0:00.01 ksoftirqd/1
16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/1:+
17 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuhp/2
18 root rt 0 0 0 0 S 0.0 0.0 0:00.01 migration/2
19 root 20 0 0 0 0 S 0.0 0.0 0:00.10 ksoftirqd/2

As we can see the kernel is not the absolutely minimum configured kernel, but it works well for our task. During this project, we spent a lot of time in configuring a new kernel from scratch and failed. Our "emergency solution" was to use this arch kernel with a real-time patch. We did not had the time to disable the unused daemons.

Real-time capability

Diagrams

On the y-axis is given the time in nanoseconds. Since we did not manage to configure our own kernel and the due date was coming, we decided to eliminate the peaks (values > 90%). Those peaks can be attributed to unintended interrupts from services we don't need. To measure the execution time, all other threads on the core weren't started. When time measuring is enabled, the threads write the measurements to a pre-allocated buffer. When everything is finished, the buffers are written to files.

After ~2000 measurements, the robot left the labyrinth and went on to look for rfid tags. Within the labyrinth, we are using pwm to control the engines whereas the rfid search only sets the gpios once per loop.

The logic thread controls the engine thread based on the measured values from the sensing thread. The execution time is pretty constant. Lower execution times orginate from turns. While the robot turns, this thread skips the unnecessary calculations.

We don't know how the ultrasonic sensor works. The behavior will remain inexplicable to us.

Polling is a constant process. :)

Everytime, the robot detects a rfid tag, such a peak appears. Because it takes some time to leave from the tag, the library is optimized to skip calculations when the tag is known.

To measure the execution time when the system is stressed, we used the stress script from CPU burn arm.

Since we did not manage to use the EDF-scheduler properly, the following plot shows that the exection time in a stressed system is much higher than without stress.

Tasksets

In total, our program creates three tasksets. Each taskset is assigned to an isolated core (isolcpus). Taskset 1 contains one thread which exploits the measurements of the sensors and decides what to do and the killswitch thread. Taskset 2 contains only one thread which controls the two engines. Taskset 3 contains three threads which read out the sensors (Infrared, RFID, Ultrasonic).

Measurements

Task Taskset T_eMin [ms] T_eMax [ms] T_pMin [ms] = T_dMax
Logic compute 1 0.001927 5.071824 30
Killswitch listener 1 0.033594 0.110103 1
Engine control 2 0.119791 20.324052 100
RFID 3 0.322289 13.108615 35
Ultrasonic 3 0.721034 10.275833 50
Infrared (all 4) 3 0.130103 0.233956 35

Sufficient scheduling test EDF

Inserting the measured values from taskset 1 into the formula for the necessary real-time condition, we get maximum coefficient of: 0.228. Seeing that the engine control is the only thread on its cpu and the wcet is lower than its deadline, we forego a scheduling test for this set. Taskset 3 delivers a coefficient of 0.5867. Hence, all tasksets accomplish the sufficient scheduling test and do not require a necessary scheduling test.