MTRN3500 Assignment 2
User interface programming - Ground Vehicle Simulator
1 Aims
- To implement an object-oriented program using C++.
- To make proper use of an industry standard 3D graphics library (OpenGL).
- To interface with a live stream of data to control a graphical object and display it in its environment.
2 Background
Modelling of custom mechanical, electrical and autonomous systems can be a beneficial step to understanding a system. While CAD software provides a good design tool, modelling of live systems specifically for control and status monitoring can be more efficient under purpose built software rather than modifying existing software to meet the system’s requirements. This assignment will require you to extend the given 3D world modelling software to model the motion of two or more ground vehicles. You will then be required to read data streams or an input device in real-time, use the data to control the graphical ground vehicles and display them in their environments.
3 Supplied Code Overview
You should begin this assignment by downloading the base code ZIP file from Moodle. A number of files have been provided to allow you to view and navigate a simple 3D world so you can test your code. See Section 4 for details on downloading and setting up the given base code.
Once the downloaded code is up and running the list of controls in Table 1 will allow you control the vehicle and to move the virtual camera.
Table 1: Base code commands
Control |
Description |
Arrow keys |
Drive vehicle forward/left/backwards/right. |
W,A,S,D |
Move camera forward/left/backward/right. |
C |
Descend camera vertically. |
(space) |
Ascend camera vertically. |
Mouse drag |
Rotate the camera’s viewing direction. |
0 (zero) |
Move the camera to the origin. |
P |
Move the camera to vehicle pursuit position. |
Additionally, you can exit the program by pressing Escape. See the use of the “KeyManager” class in the main source file for more information on how keyboard events are linked to the virtual OpenGL Camera. In short, keys that are pressed once (such as getting the virtual camera to move to the origin) are handled the normal way via GLUT (OpenGL Utility Toolkit). To handle multiple keys being held down at the same time, the additional functionality of the custom “KeyManager” class is used.
Usually when modelling a vehicle’s pose and motion from live sensor data you would acquire the data from a suite of on board sensors, a centralised database or a network socket. For this assignment, you will be reading data from a data server. An emulator and a sample data set will also be provided to allow you to test your application outside of the laboratory environment.
The “Shape” class provided should be used as the parent or base class for all shapes outlined in Section 5.1. For simplicity, all shapes you create will have a 3D position using (x,y,z) coordinates and a yaw angle in degrees. Yaw is the rotation in the horizontal plane, which in this case is the XZ-plane. Zero degrees of rotation means you are facing a direction parallel to the positive x-axis. The vertical axis is the y-axis and we are using a right hand coordinate system.
The “Vehicle” class should be used as the parent or base class for designing vehicles as outlined in Section 5.2. The base vehicle object uses a basic mathematical model to translate speed and steering values from input devices such as the keyboard or an Xbox controller to motion. Your initial task is to practice writing derived classes that represent different vehicle designs. See Section 5.2 for further details.
After you have defined a custom class deriving from the Vehicle class you can test it by finding the following section of code in the main() function in the file main.cpp:
// vehicle = new MyVehicle();
You should replace “MyVehicle” with the name of your custom vehicle class and uncomment the line of code. This will enable the rest of the keyboard and drawing functions to be linked with your custom vehicle class.
4 Setting Up
The following steps will get you up to compiling and running the base source code provided on Moodle. There is a separate section for each of Windows, Linux and Mac OS X. Although there are different steps for each operating system, all of the base assignment code can be found in the AssignmentGL-Base.zip file on
Moodle. All supporting headers, libs and dlls can be found in the AssignmentGL-Support.zip file on Moodle.
4.1 Windows
These steps are based on Visual Studio 2010, but should also work for later Visual Studio versions. As a student of the University of New South Wales, you are eligible to download a free student-licensed version of Visual Studio Professional by visiting Microsoft Dreamspark (www.dreamspark.com) online.
4.1.1 Setting up a new project
- Open Visual Studio and create a new project.
- From the Templates panel on the left select Visual C++ and then Win32.
- Select Win32 Console Application.
- Enter a project Name. (For example “Assign2”).
- Choose a Location to place the project.
- Leave the Solution Name the same as the project Name.
- Click OK.
- Click Next to go to the “Application Settings” page.
- Make sure Application Type is “Console Application”. Make sure Additional options has “Empty project” checked and “Precompiled header” and everything else unchecked. Make sure Add common header files for has every option unchecked.
- Click Finish to finish setting up the project.
4.1.2 Including the base source code
- Download the Assignment ZIP (AssignmentGL-Base.zip) file and extract to somewhere easy to find.
- Back in the new project you’ve just created find the Solution Explorer. If you cannot find the Solution Explorer go to the View menu then click Solution Explorer.
- A cpp file will have been created with the same name as the project, for example Assign2.cpp. Open this file and delete the contents (we will be using our own pre-defined main function).
- In the Solution Explorer right-click “Header Files”, select “Add” and then “Existing Item”.
- Locate the folder where you extracted the Assignment ZIP files. Highlight all the hpp files and click “Add”. (You can select a group of files by holding Control and clicking on each file).
- In the Solution Explorer right-click “Source Files”, select “Add” and then “Existing Item”.
- Locate the folder where you extracted the Assignment ZIP files. Highlight all the cpp files and click “Add”.
All the given header and source files are now set up and included correctly.
4.1.3 Setting up OpenGL headers, libs and dlls
- Download the AssignmentGL-Support.zip file from Moodle and extract the files to an easy to find location. In the Win32 folder there should be three folders: dlls, include and lib.
- Copy the include and lib folders to an easy to find location, for example C:\include and C:\lib.
- Open the dlls folder and copy the contents to C:\Windows\System32. Alternatively you can place the dll files in the same directory that the compiled exe file will eventually be compiled to. If you keep Visual Studio in “Debug” mode (this is the default mode), the exe will eventually be compiled to a “Debug” folder in your project files.
4.1.4 Compiling and Linking with OpenGL
- Back in the Visual Studio project open project properties (either via Alt+F7 or via the “Project” menu then “Assign2 Properties”).
- Navigate to the Configuration Properties, C/C++, General
- Set “Additional Include Directories” to the location where you placed the GL files include directory, for example C:\include.
- Navigate to the Configuration Properties, Linker, General
- Set “Additional Library Directories” to the location where you placed the GL files lib directory, for example C:\lib.
- Navigate to the Configuration Properties, Linker, Input
- On “Additional Dependencies” click the down arrow and the “Edit”.
- Add to the textbox, one per line: lib, glu32.lib, and glut32.lib
- Click OK. 10. Click OK.
Everything should be properly set up and ready to compile and run. Press F5. This should compile and then run the program. If a window appears then it worked. Try and move around the basic 3D world using the controls outlined earlier in Table 1 of this document.
If it didn’t work look at the Output window, usually at the bottom of Visual Studio. If the Output window is not visible you can select it from the “View” menu. If you can self diagnose the problem that is great, but you are also encouraged to post a topic on the Assignment Two Discussion Board in Moodle.
4.2 Linux and Mac
These steps are based on using the g++ compiler.
Linux and Mac OS X are relatively the same, except on most Linux distros the compiler and libraries are installed by default. In Linux, if you try to run the command g++ and the shell complains that it cannot find the command, you should install the compiler by running the following command:
Ubuntu/Debian:
apt-get install build-essential RedHat:
rpm install build-essential
On Mac OS X you will need to install Xcode. Xcode is available via the App Store for free, but a link can also be found on http://developer.apple.com if you have a free Apple developer licence. Xcode is something like 3 or 4 GB, so be warned.
4.2.1 Setting up a folder structure
- Create a new directory called, for example, assign2.
- Download the AssignmentGL-Base.zip file and extract the hpp and cpp files to the newly created directory.
4.2.2 Setting up OpenGL headers and libs
On Mac OS X, downloading Xcode will automatically set up OpenGL, GLU and GLUT in the correct place. On Linux you should be able to apt-get install (Debian/Ubuntu) or rpm install (RedHat) the correct packages. If on Linux and you can’t install the headers and libs using a packing service, use the following backup steps:
- Download the AssignmentGL-Support.zip file from Moodle and extract the files to an easy to find temporary location. In the Linux folder there should be two folders: include and lib.
- Copy the include folder to a location that’s easy to find (for example: ∼/include).
- Copy the lib folder to a location that’s easy to find (for example: ∼/lib).
Please ask for assistance in the Assignment Two Discussion Board on Moodle if you run into problems.
4.2.3 Compiling and linking with OpenGL
It is recommended to put the following text in a Makefile file in the assignment code directory.
LIBS = -lGL -lGLU -lGLUT
SRC = <list all your cpp files>
LIBDIR = -L∼/lib INCDIR = -I∼/include all:
<tab> g++ -o run $(SRC) $(LIBS) $(LIBDIR) $(INCDIR) -g
On Mac OS X you may not need to specify the LIBDIR or INCDIR for OpenGL support files as Xcode might have set them up properly for you. Also, Mac OS X users might find it easier developing directly in Xcode instead of using g++ directly. There are plenty of tutorials online for setting up an OpenGL/GLUT project with Xcode, but if you need further assistance, please ask in the Assignment Two Discussion Board on Moodle.
By creating a Makefile you can simply type make at the command line in the assignment directory and it will compile and link your code to from an executable. Remember, when you add more cpp files make sure you make the necessary changes to the Makefile as well. You can then run the program by typing:
./run
5 Part 1 - Constructing Shapes and a Vehicle
5.1 Creating 3D Shapes
Your first task is to extend the Shape class to implement a set of basic 3D shapes. When we say extend we mean the object oriented programming term (i.e. class derivation) rather than adding a large chunk of code to the Shape class itself. The Shape class contains position and orientation attributes, which are common to every 3D object. You need to implement classes at least for the following shapes outlined in this document. For simplicity, each object can be assigned one colour using three floats to specify the red, green and blue components of the colour. Look at the “Shape.hpp” file for more details on how to interface with and extend the Shape object.
5.1.1 Rectangular Prism
This shape should make use of three additional member attributes concerning the length of the shape in the three spatial dimensions (for example, x length, y length and z length). You should also devise how the volume of the prism is positioned relative to it’s internal x, y and z attributes and how the rotation variable rotates the object in the horizontal plane. One suggestion could be to define the (x,y,z) location as the center of the object and the object’s rotation is applied about the origin of the object.
5.1.2 Triangular Prism
This shape should make use of additional member attributes to specify the dimensions of the triangular prism, the choice of which is left up to you. The length of the prism is an obvious choice for one of these attributes, but you should determine a method for specifying the shape of the triangular dimensions of the object. For example, two different approaches, among others, are to store:
- the three side lengths of the triangle, or
- two side lengths and an angle.
You should also remember to intelligently decide on a center for your object and how this relates to object rotation.
5.1.3 Trapezoidal Prism
You should decide on additional member attributes to specify the shape of this object. In particular you need to decide on a way of specifying the dimensions of the trapezium at the end of the prism in an efficient way.
5.1.4 Cylinder
You can use additional member attributes such as length or height along with radius to specify the dimensions of a cylinder. You may make use of the inbuilt “gluCylinder” or “glutCylinder” functions. A cylinder for this project must be a solid cylinder. That is, it should consist of a curved surface together with two circles on each end of the cylinder. The center of a cylinder might be defined as the point half way along the central axis of the cylinder for rotation purposes.
5.2 Vehicle Modelling
Using the collection of basic 3D shapes have defined in the previous section, you should implement some models of different types of vehicles. When implementing a model of a vehicle you should extend from the base “Vehicle” class provided with the initial collection of files. When constructing the model from basic shapes you should position shapes in the vehicle’s local frame of reference. As a result, the “draw” method in your custom vehicle class should contain the following general structure:
{`void MyVehicle::draw()
{
// move to the vehicle’s local frame of reference glPushMatrix(); positionInGL();
// all the local drawing code
// move back to global frame of reference glPopMatrix();
}
`}
The base “Vehicle” class contains an “update” function which handles interpreting control input into vehicle motion using a basic mathematical model. Although it is not required, you may modify or improve this mathematical model in the “Vehicle” class. The “update” function, combined with the drawing structure above will position and orient the vehicle’s model correctly in 3D space. You need to make sure you position shapes so that the vehicle is facing the positive x-axis in it’s local frame of reference. For best results make your vehicles not longer than 4 units and not wider than 3 units.
However, if your vehicle has wheels (which it probably will), you must make the wheels rotate at a rate that approximately resembles the vehicle’s speed. Additionally, if your vehicle has wheels that can steer when steering then you also should make the models wheel’s steer proportional to the steering angle. When your work is being assessed, we will give you a model of vehicle (hereafter called the local vehicle) that contain both wheels that rotate and wheels that steer. In addition you will also receive one or more vehicle models (here after called remote vehicles) from a data server. You need to display all these vehicles on the screen. See Section 8 for more details.
As good preparation, you could implement some prefabricated vehicle parts to make constructing vehicle models quicker. For example, a “Wheel” class could be created that contains a cylinder wheel with either cylindrical or prism based spokes for the wheels to tell they are turning when the vehicle is in motion. The more prepared you are beforehand, the easier the assessment process will be.
5.3 Final Checklist for Part 1
- Create a RectangularPrism class extended from Shape class.
- Create a TriangularPrism class extended from Shape class.
- Create a TrapezodialPrism class extended from Shape class.
- Create a Cylinder class extended from Shape class.
- Instantiate local/remote vehicles extended from Vehicle class.
- Vehicles should have wheels that roll when driving forward/backward.
- Vehicles should have front wheels that steer when steering.
6 Part 2 - Using Data from the Data Server
This part deals with making the remote vehicles move. (See Section 7 to see how to make the local vehicle move). In order to do this you will need to interface with a data source over an Ethernet/Wi-fi connection. We have provided a class (called RemoteDataManager) and a set of functions (declared in Messages.hpp) that hides away most of the low-level implementation detail for you. The RemoteDataManager class will either connect to a local host or over the internet to the UNSW robotics server www.robotics.unsw.edu. Communication with the server is bidirectional, and is used to synchronise your environment with the server’s. To enable communication with the data server, uncomment the relevant line of code in the idle() functions in the main.cpp file near this line:
//RemoteDataManager::Connect("127.0.0.1","18081");
Most of the messages received from the data server will be handled by the provided code, with the exception of the “M”-vehicle model message. The “M” message contains one or more VehicleModel objects represented according to the data structures given in Table 2. You will need to process each model object to instantiate the remote vehicles correctly, and in each model object, you will need to process the shape information. The relevant section of code can found in the main() function in the file main.cpp near this line:
//otherVehicles[vm.remoteID] = new MyVehicle();
You should replace ”MyVehicle” with the name of your custom vehicle class and uncomment the line of code. This will enable remote vehicles to be added to the map of other vehicles to be drawn and updated.
As can be seen, the information here is not any different to the information you used to draw the local vehicle. Hence, you can use the parts of software you used to draw the local vehicle to draw these remote vehicles in an identical manner provided you match up the data received from the data server to your own vehicle data representation. As soon as you have drawn your remote vehicles, they will begin to move according to the state data streaming from the data server. This part has already been completed for you and you therefore do not have to do this part. However, what you need to do is to fill in a structure according to the one given in Table 3 and send it to the data server to report your local vehicle status. The functionality to send the data will be provided by the RemoteDataManager software. You need to provide the code to fill in the data structure, given below in Table 3. If the data you provide is beyond nominal range, the server may respond with an error message and terminate your connection.
6.1 Final Checklist for Part 2
- Implement code in your graphical application to receive data server message “M” and display one or more vehicles accordingly with the correct shapes.
- Implement code in your graphical application to report your local vehicle status to the server.
Table 2: “M” Message
{``}
enum ShapeType {
UNKNOWN_SHAPE,
RECTANGULAR_PRISM, TRIANGULAR_PRISM,
TRAPEZOIDAL_PRISM,
CYLINDER };
union ShapeParameter
{ struct RectangularParameters
{ float xlen; // length along x-axis float ylen; // length along y-axis float zlen; // length along z-axis
} rect;
struct TriangularParameters
{
float alen; // length of side A (bottom) float blen; // length of side B (left) float angle; // angle (degrees) between side A and B float depth; // length along z-axis
} tri;
struct TrapezoidalParameters
{
float alen; // length of side A (bottom) float blen; // length of side B (top) float height; // distance between side A and B
float aoff; // distance A is shifted from B by, from the left float depth; // length along z-axis
} trap;
struct CylinderParameters
{ float radius;
float depth; // length along z-axis
bool isRolling; // needs to roll with vehicle?
bool isSteering;// needs to steer with vehicle? } cyl;
};
struct ShapeInit
{
ShapeType type; ShapeParameter params; float xyz[3]; float rotation; float rgb[3];
};
struct VehicleModel
{ int remoteID; std::vector<ShapeInit> shapes; 8
};
Table 3: “S” (State) Message
struct VehicleState
{`{ int remoteID; // this should be 0 for local vehicles float x; float z; float rotation; float speed; float steering; }`};
7 Part 3 - Game Controller Interface to Control Your Local Vehicle
The final part of the assignment requires you to implement vehicle control interface to control the local vehicle. You need to develop a GameController class based on the sample code provided. You may also use your own game controller code. The aim is to generate a speed and steering command for the local vehicle and then to use those commands to drive the local vehicle. You may design the GameController interface how you see fit, but it is recommended to have at least the following functionality:
- A way to incorporate the game controller outputs to control the local vehicle pointed to by the pointer vehicle mentioned earlier in this document. Incorporate a method called update() which is called in the main.cpp file’s idle function, which will update your local vehicle’s pose as per the speed and steering command from the game controller.
Vehicle speeds will range from 0 to 10 ms−1 and steering angles will range between −15o and +15o degrees.
7.1 Final Checklist for Part 3
- Design and implement a GameController interface to generate vehicle speed and steering.
- Interface the GameController outputs to control local vehicle motion when a controller is connected.
8 Assessment
- You are required to prepare a set of 3D object and practice constructing vehicles from the objects inyour own time.
- You must get your work marked by a demonstrator during Week 10. The penalty for late assessment is 2 marks per week or part thereof.
- You must make an appointment with your demonstrator by the end of Week 8. If you miss this deadline, the penalty is one mark per calendar day. DO THIS WELL AHEAD OF TIME
- During assessment you will be required to bring in all of your source code.
- At the time of assessment your assessor will give you the specifications for the local vehicle which youwill then need to model using your developed classes. You will be given 20 minutes to prepare this model and test in your program. You will have incorporated all other functionality required before arriving at the assessment. As such when you connect to the data server, we should see the remote vehicles on the screen and they should begin to move automatically.
- After such time your demonstrator will check the model you created to see if it appears to be correct to specification and any moving parts properly animate under vehicle motion.
- Your demonstrator will also check the reporting of your local vehicle status to the data server.
- You will then be asked to use your GameController to follow a remote vehicle using the local vehicle.
- Your demonstrator will then ask you to explain your code and class structure. Marks will be awardedaccording to the following scheme:
- Making the program compile and be fully operational on the day of assessment (2 marks).
- Whether the model looks and animates correctly (2 marks).
- Whether the live feed of data from the data server is correctly read and used (2 marks).
- Whether the local vehicle’s status is correctly reported to the data server (1 mark).
- Whether the GameController interface is properly operational (1 mark).
- Modularity: is the program broken up into well defined files, classes, functions? (1 marks).
- Structure: Is the code efficient and logical? (1 mark).
- Program constructs: Did you make use of the correct C++ construct or command for a particulartask? (1 mark).
- Did you clearly explain the program to your demonstrator? (4 marks).
9 Bonus Marks
You will get two bonus marks if you implement a successful autonomous car-following pursuit algorithm, in addition to the game controller driven pursuit. An auto pursuit algorithm will make the local vehicle autonomously follow a chosen remote vehicle without the need of any game controller or keyboard inputs.
9.1 An Example Vehicle to Model
Below is a sample specification for a simple vehicle.