Title: 3. Controlling Robot Car by Wireless Sensor
13. Controlling Robot Car by Wireless Sensor
- The ultimate objective of the project is to
control the car motion using the steering wheel - The only connection between the steering wheel
and car racing system is the wireless marker
attached to it - We have to make use of the marker data to control
the car motion
Reference 1. J.M. Hart, Windows System
Programming, 3rd Ed., Addison-Wesley, 2005, Ch.7,
12
2Problem of the Previous Program
- The markers data are obtained by calling the
function LatusUpdatePO() - In the previous program, LatusUpdatePO() are
called inside the functions EIE330ProjectApprend
erInfo and - EIE330ProjectAppprocessCalculation()
- The above 2 functions will be called by Ogre only
when it updates the screen - If the graphics is complex, the update rate can
be very slow, e.g. 20 frames/sec or slower - It means that the markers data can only be
obtained in such a slow rate
3Update screen
Ogre main program
processCalculation()
mLatusUpdatePO()
If the screen is updated once per 100ms, markers
data can only be obtained in the same rate, i.e.
once per 100ms
renderInfo() mLatusUpdatePO()
Finish update screen
4Solution - Multithreading
Update screen
Get Markers data
Ogre main program
processCalculation()
UpdatePO()
renderInfo()
Routine A and B run at the same time (virtually)
Routine B
Routine A
Finish
Finish update screen
5Processes and Threads
- In Windows, usually a process will be generated
when an application is executed - When an application is executed m times, m
processes will be generated, each with a
different process ID - A Windows process contains its own independent
virtual address space with both code and data - Each process contains one or more independently
execution unit, called threads - The Windows thread is the basic executable unit
- A process can
- Create new threads within the processes
- Create new, independent processes
- Manage communication and synchronization between
these objects
6A Process And Its Threads
Windows
Process 1
Winword
Process 5
Process 2
Lab3
Notepad
Threads
Process 3
Process 4
Winword
Excel
7Why Threads?
- In normal execution, a program always needs to
wait - Wait for user input, wait for screen display,
wait for file access, etc. - It is unwise to require programs to execute one
after the finish of another - Program B can make use of the waiting time of
program A to start its execution as long as
program B is not waiting for the result from
program A - It was first proposed to achieve this by having
multiple processes - However, it was soon found that the overhead
(e.g. the time required) for switching between
processes is very high - Besides, processes are not tightly coupled to one
another, it is difficult to share resources, such
as open files - They motivate the idea of further dividing a
process into smaller units, i.e. threads
8How Threads Are Executed?
- A computer has only one CPU, which can execute
one program at a time - Hence, in reality, threads are not executing at
the same time, but alternatively one after the
other - For a multithreading system, a thread has at
least the following three states
Ready
Running
Start execution
Finish execution
Sleeping
9- Ready state
- All threads that are ready to execute but without
the CPU are at the ready state - If there is only 1 CPU in the system, all threads
except one are at the ready state - Running state
- The thread that actually possesses the CPU is at
the running state - If there is only 1 CPU in the system, at most
there is only one thread is at the running state - Sleeping state
- The process that is waiting for other resources,
e.g. I/O or a preset time, is at the sleeping
state
10- Threads will alternatively get into the CPU one
after the other (called the round robin scheme) - At the time that a thread is selected to be in
the CPU - It goes from ready state to running state
- After that, it will be swapped out
- It goes from running state back to ready state
- Or it may due to the waiting of a preset time
- It goes from running state to sleeping state
- When time-up
- It goes from sleeping state to ready state
11Implementation - CreateThread
- HANDLE CreateThread (
- LPSECURITY_ATTRIBUTES lpsa,
- DWORD dwStackSize,
- LPTHREAD_START_ROUTINE lpStartAddr,
- LPVOID lpvThreadParm,
- DWORD dwCreationFlag,
- LPDWORD lpThreadId )
Example
HANDLE mThread // Handle to thread DWORD
mThreadId // Use to store the thread
id UpdateParaStruct pUPS new UpdateParaStruct
// Define a structure to store passing
parameters mThread CreateThread(NULL, 0,
RunningUpdateThread, (LPVOID)pUPS, 0,
mThreadId)
12CreateThread Parameters
- Input parameters
- lpsa A pointer to a security attribute
structure. It determines whether the returned
handle can be inherited by child processes. If
lpsa is NULL, the handle cannot be inherited - dwStackSize Indicate threads stack size. Use 0
for default size - lpStartAddr A pointer to the function to be
executed. For our example, the function
RunningUpdateThread will be executed.
RunningUpdateThread() should be defined as
follows
DWORD WINAPI RunningUpdateThread (LPVOID
parameter)
13CreateThread Parameters
- lpvThreadParm A pointer passed as the thread
argument. For our example, the pUPS pointer, i.e.
the pointer of the structure UpdateThreadStruct,
is passed to the thread function. - dwCreationFlag indicate the readiness of the
thread. If 0, means that the thread is ready to
run immediately - lpThreadId A pointer to a DWORD that receives
the new threads identifier. The system will fill
in it with the thread ID - CreateThread will return the handle to the thread
created. A NULL handle value indicates a failure
14Passing Parameters to Thread
- A thread function only accepts one input
parameter, i.e. lpvThreadParm - If more than one parameters are to be passed to a
thread function, the most convenient way is to
put them in a struct and pass its pointer, e.g.
struct UpdateParaStruct // For passing
2 data to the Update thread Latus pL bool
pExit
15Receive the Passed Parameters
- When the thread function receives this pointer,
it should first tell the function what kind of
pointer it is - Then retrieve the parameters stored in the
structure
DWORD WINAPI RunningUpdateThread (LPVOID
parameter) UpdateParaStruct pUPS
(UpdateParaStruct ) parameter Latus pLatus
pUPS-gtpL bool pExit pUPS-gtpExit
16To Control the Execution Rate of a Thread
- To control the execution rate, the simplest way
is to force the thread to sleep for some time
DWORD WINAPI RunningUpdateThread (LPVOID
parameter) UpdateParaStruct pUPS
(UpdateParaStruct ) parameter Latus pLatus
pUPS-gtpL bool pExit pUPS-gtpExit
while (true) pLatus-gtUpdatePO() Sleep(10)
// Force to sleep for 10msec return 0
For the about thread, assume the other part of
the program uses negligible amount of time, the
execution rate of this thread is approximately
once per 10msec
17How a Thread Terminates
- Most common way
- A thread terminates itself by returning from the
thread function using the exit code as the return
value
DWORD WINAPI RunningUpdateThread (LPVOID
parameter) return 0 //exit code
0 //Never return 259 since it is equal to the
// constant STILL_ACTIVE, which is used
to // indicate the thread is still active
18How to Know if a Thread is Still Alive
- We can check if a thread is still alive by
calling GetExitCodeThread(), e.g.
HANDLE hThread // Handle to thread DWORD
ThreadId // Use to store the thread
id UpdateParaStruct pUPS new
UpdateParaStruct hThread CreateThread(NULL,
0, StartPlayer, (LPVOID)pUPS, 0,
ThreadId) DWORD exitCode if(GetExitCodeThre
ad(hThread, (LPDWORD)exitCode)) stdcout ltlt
exitCode ltlt stdendl //GetExitCodeThread will
return nonzero if successful // The exit code is
stored in exitCode
19Force a Thread to Terminate
- The simplest way is to call the function
TerminateThread(), but many side effects - Another way is to let the thread function to
check frequently a shared variable which can be
modified by the thread outside
DWORD WINAPI RunningUpdateThread (LPVOID
parameter) UpdateParaStruct pUPS
(UpdateParaStruct ) parameter Latus pLatus
pUPS-gtpL bool pExit pUPS-gtpExit
while (!(pExit)) return 0
Assume pExit is a pointer of a shared variable
which will be updated by a thread outside. If
that thread wants this thread to terminate, it
just needs to change such variable to TRUE
20Thread A
21Software Architecture for Lab 3
Thread 1
Thread 2
EIE330ProjectApp
UpdateParaStruct
RunningUpdateThread
Latus Exit 1 Exit 2
Game
mLatus-gtUpdatePO()
mLatus-gtAnalysis()
ActionParaStruct
mLatus mGame
Thread 3
RunningActionThread
Screen Update
mLatus-gtAction()
22Analysis()
- After receiving the markers data, we should
analyze the data to determine the kind of action
we should perform - The analysis is supposed to be done in a function
LatusAnalysis() which should be called after
each call to UpdatePO() - The analysis result should be put in an array of
ActionStruct structs, each struct has 2 fields
23Array of ActionStruct
ActionStruct mActionTable3
- Based on the analysis result, a number should be
put by the function LatusAnalysis() to the type
of an ActionStruct to indicate the kind of action
to be performed. - E.g.
- type 1 go forward
- type 2 go backward
- type 3 turn right
- type 4 turn left
type
data
1 0
4 0
3 0
mActionTable0
mActionTable1
mActionTable2
- For some actions, you may want to provide
additional data - Put it in the data field
- In some cases, you may want to define a sequence
of operations for each analysis result - Do it by filling in all 3 structs
24Action()
- After the action table is filled, the function
LatusAction() should be called to carry out the
operations defined in the table one by one - Since the car motion is much slower than the data
received from the markers, the function Action()
can be called in a much slower rate - A separated thread is used to called Action()
called in thread function RunningActionThread() - The rate of execution of this thread should match
with the command execution rate of Mindstorms