-
Notifications
You must be signed in to change notification settings - Fork 27
Step by step approach to simulation added #251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,11 @@ Official repository: https://github.com/boostorg/website-v2-docs | |
= Real-Time Simulation | ||
:navtitle: Simulation | ||
|
||
Creating a real-time simulation of vehicles involves various aspects, including physical modeling, concurrent programming for real-time response, data storage and manipulation, networking for multi-vehicle simulation, and perhaps a graphic interface. Here are some libraries that may be helpful: | ||
Creating a real-time simulation of objects such as vehicles or processes such as pipelines involves various aspects, including physical modeling, concurrent programming for real-time response, data storage and manipulation, networking for multi-vehicle simulation, and perhaps a graphic interface. | ||
|
||
== Relevant Libraries | ||
|
||
Here are some libraries that may be helpful: | ||
|
||
[circle] | ||
* boost:chrono[]: Timing is critical in real-time applications. This library can help you measure time intervals, which could be useful for controlling the timing of your simulation. | ||
|
@@ -20,10 +24,365 @@ Creating a real-time simulation of vehicles involves various aspects, including | |
|
||
* boost:graph[]: In case you need to represent roads or pathways as a graph, this library provides a flexible and powerful way to represent and manipulate graphs. It also includes a number of graph algorithms. | ||
|
||
* boost:thread[] or boost:asio[]: To achieve real-time performance, you might need to make use of multi-threading or asynchronous input/output. boost:thread[] provides classes and functions for multi-threading, synchronization, and inter-thread communication. boost:asio[] is a cross-platform library for asynchronous programming and can handle a lot of networking tasks as well. | ||
* boost:thread[] or boost:asio[]: To achieve real-time performance, you might need to make use of multi-threading or asynchronous input/output. boost:thread[] provides classes and functions for multi-threading, synchronization, and inter-thread communication, and would be useful to parallelize calculations for physics updates. boost:asio[] is a cross-platform library for asynchronous programming and can handle a lot of networking tasks as well. | ||
|
||
* boost:interprocess[]: If you need to share data between different processes in real-time, this library can be useful. It supports shared memory, memory-mapped files, semaphores, and more. | ||
|
||
* boost:mpi[] or boost:asio[]: For distributed simulations that run across multiple systems, you might need a library for network communication. boost:mpi[] provides a pass:[C++] interface for the Message Passing Interface (MPI) standard for distributed computing. boost:asio[] can also handle networking tasks and it is a bit lower-level. | ||
|
||
* boost:serialization[]: To save the state of the simulation or to communicate complex data structures over a network, you might find this library helpful. | ||
|
||
* boost:random[]: It is often helpful to introduce "noise" in a simulation to mimic real-world unpredictability, such as initial conditions, changes to weather patterns, modelling uncertainty, and reducing complex computations to simple statistical chances. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boost.Random and std's |
||
|
||
* boost:pool[]: To manage dynamic allocation of objects like particles, forces, and other entities. Useful if your simulation requires the creation and deletion of many objects. | ||
|
||
== A Step-by-Step Approach | ||
|
||
Developing a good real-time simulation of real-world objects is not a linear task, but is iterative. First attempts at physical modelling are unlikely to give fully acceptable results, so plan on going round in circles for some of the development time as algorithms and data sources are refined. What follows is a step-by-step approach, pulling in various Boost libraries when they are needed. | ||
|
||
The critical component of a simulation is timing, so let's start by committing to boost:chrono[] for this, and building from there. | ||
|
||
=== Define Project Scope and Requirements | ||
|
||
Before diving into any code, clearly define the scope and requirements of your simulation. Writing this to a design specification document is a great idea: | ||
|
||
- Determine the level of real-time accuracy required. | ||
- Define the physical properties and behavior of the vehicles and objects required. And perhaps the properties that are _not_ required. | ||
- Specify the environment in which the simulation will operate (for example, flat surface, complex terrain). | ||
- Determine how user input will be managed (for example, keyboard, mouse, game controller). | ||
- Decide on the output format (for example, graphical display, data logging) | ||
|
||
Specify the physical quantities involved (for example, speed, acceleration, force) and their units. We will leverage boost:units[] to manage these. | ||
|
||
At this stage, consider whether your simulation requires spatial computations. If you need to handle tasks such as collision detection, routing, or spatial queries, boost:geometry[] will be a good companion. | ||
|
||
Identify the I/O requirements of your simulation. Determine if you need to handle asynchronous communication with external devices, network communication, or user inputs that should not block the main simulation loop. If any of these are true, let's look to boost:asio[] to manage these operations. | ||
|
||
=== Set Up Your Development Environment | ||
|
||
Ensure your development environment is properly set up, notably: | ||
|
||
- Use a modern pass:[C++] compiler that supports pass:[C++]11 or later. | ||
- xref:getting-started.adoc[Install all Boost libraries], so there will be no dependency issues when you add a library that you did not originally plan for. | ||
- Choose an Integrated Development Environment (IDE) or text editor that you are comfortable with. | ||
|
||
=== Structure Your Project | ||
|
||
Create a basic structure for your project to keep things organized. At this stage we can start pulling in individual Boost libraries into our plan: | ||
|
||
- The _Main Program_ contains the main loop and initialization code. | ||
- A _Vehicle Class_ manages vehicle dynamics and state. | ||
- A _Simulation Class_ manages the overall simulation logic. | ||
- A _Timing Class_ utilizes boost:chrono[] to handle timing. | ||
|
||
Create a module or namespace for handling physical measurements using boost:units[]. | ||
|
||
You may also need classes to handle spatial data structures to represent geometric entities such as points, lines, and polygons, and spatial operations such as modules for performing geometric computations. We will be looking to boost:graph[] and boost:geometry[] to help out here. | ||
|
||
If needed, plan for a dedicated I/O handler that uses boost:asio[] for managing asynchronous operations. | ||
|
||
=== Implement Timing Control with Boost.Chrono | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same with Boost.Chrono and |
||
|
||
Start by implementing the timing control, and make the decision on the granularity of the timing, matching 60 FPS (Frames Per Second) in the example below: | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/chrono.hpp> | ||
#include <iostream> | ||
|
||
class Simulation { | ||
public: | ||
Simulation() : last_time(boost::chrono::steady_clock::now()) {} | ||
|
||
void run() { | ||
while (running) { | ||
auto current_time = boost::chrono::steady_clock::now(); | ||
auto elapsed = boost::chrono::duration_cast<boost::chrono::milliseconds>(current_time - last_time).count(); | ||
|
||
if (elapsed >= timestep) { | ||
update(elapsed); | ||
last_time = current_time; | ||
} | ||
} | ||
} | ||
|
||
private: | ||
void update(int64_t elapsed) { | ||
// Update vehicle dynamics and state here | ||
std::cout << "Elapsed time: " << elapsed << " ms" << std::endl; | ||
} | ||
|
||
bool running = true; | ||
const int64_t timestep = 16; // ~60 FPS | ||
boost::chrono::steady_clock::time_point last_time; | ||
}; | ||
|
||
int main() { | ||
Simulation sim; | ||
sim.run(); | ||
return 0; | ||
} | ||
---- | ||
|
||
Note:: boost:asio[] will later complement this by handling asynchronous I/O without blocking the timing updates. | ||
|
||
=== Develop the Vehicle Dynamics | ||
|
||
Next, focus on developing the physical vehicle or process dynamics: | ||
|
||
- Implement the physics equations governing the vehicle's motion (for example, Newton's laws, friction, acceleration). | ||
- Create a class to maintain the _state_ of each vehicle (position, velocity, orientation). | ||
|
||
When you develop vehicle dynamics, you will also start to see where spatial computations come into play. Use boost:geometry[] to manage and update the vehicle's position in space. Implement collision detection algorithms to ensure the vehicle interacts correctly with the environment. | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/geometry.hpp> | ||
#include <boost/geometry/geometries/point.hpp> | ||
#include <boost/geometry/geometries/polygon.hpp> | ||
#include <boost/units/systems/si.hpp> | ||
#include <boost/units/io.hpp> | ||
|
||
namespace bg = boost::geometry; | ||
namespace bu = boost::units; | ||
namespace si = boost::units::si; | ||
|
||
class Vehicle { | ||
public: | ||
Vehicle() | ||
: position(0.0, 0.0), velocity(10.0 * si::meters_per_second) {} | ||
|
||
void update(int64_t elapsed) { | ||
double time_in_seconds = elapsed / 1000.0; | ||
auto distance = velocity * time_in_seconds * si::seconds; | ||
position.set<0>(position.get<0>() + distance.value()); | ||
position.set<1>(position.get<1>() + distance.value()); | ||
} | ||
|
||
bg::model::point<double, 2, bg::cs::cartesian> getPosition() const { return position; } | ||
|
||
private: | ||
bg::model::point<double, 2, bg::cs::cartesian> position; | ||
bu::quantity<si::velocity> velocity; | ||
}; | ||
---- | ||
|
||
=== Integrate Timing with Vehicle Dynamics | ||
|
||
Ensure the integration of timing control, vehicle dynamics, and geometric updates works seamlessly. | ||
|
||
[source,cpp] | ||
---- | ||
void Simulation::update(int64_t elapsed) { | ||
vehicle.update(elapsed); | ||
auto pos = vehicle.getPosition(); | ||
std::cout << "Vehicle position: (" << bg::get<0>(pos) << ", " << bg::get<1>(pos) << ")" << std::endl; | ||
} | ||
---- | ||
|
||
Consider using boost:random[] to add random events (weather changes for example) to mimic real-world unpredictability. | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/random.hpp> | ||
#include <iostream> | ||
|
||
int main() { | ||
boost::random::mt19937 gen; | ||
boost::random::uniform_real_distribution<> dist(0, 1); | ||
|
||
for (int i = 0; i < 10; ++i) { | ||
std::cout << dist(gen) << std::endl; | ||
} | ||
|
||
return 0; | ||
} | ||
---- | ||
|
||
For more complex tasks that require detailed simulation, consider using boost:graph[] to manage networks, such as road networks in a traffic simulation or wiring in electrical networks. A common use case is in coding path finding algorithms. | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/graph/adjacency_list.hpp> | ||
#include <boost/graph/dijkstra_shortest_paths.hpp> | ||
#include <iostream> | ||
#include <vector> | ||
|
||
int main() { | ||
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph; | ||
Graph g(5); | ||
boost::add_edge(0, 1, g); | ||
boost::add_edge(1, 2, g); | ||
boost::add_edge(2, 3, g); | ||
boost::add_edge(3, 4, g); | ||
|
||
std::vector<int> distances(boost::num_vertices(g)); | ||
boost::dijkstra_shortest_paths(g, 0, boost::distance_map(&distances[0])); | ||
|
||
for (size_t i = 0; i < distances.size(); ++i) { | ||
std::cout << "Distance to vertex " << i << " is " << distances[i] << std::endl; | ||
} | ||
|
||
return 0; | ||
} | ||
---- | ||
|
||
=== Implement Input Handling | ||
|
||
Handle user inputs to control the vehicle. This might involve reading keyboard or controller inputs and adjusting the vehicle's state accordingly. | ||
|
||
Leverage boost:asio[] for handling all user inputs asynchronously. This will ensure that your main simulation loop is not blocked by waiting for input, allowing the simulation to run smoothly. | ||
|
||
==== Integrate Boost.Asio for Asynchronous I/O | ||
|
||
Ensure that the vehicle's state can be updated asynchronously based on inputs received via boost:asio[]. Consider setting up asynchronous read/write operations for user input or network communication. | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/asio.hpp> | ||
#include <iostream> | ||
|
||
class AsyncInputHandler { | ||
public: | ||
AsyncInputHandler(boost::asio::io_context& io_context) | ||
: input_stream(io_context, ::dup(STDIN_FILENO)) { | ||
start_read(); | ||
} | ||
|
||
void start_read() { | ||
boost::asio::async_read_until(input_stream, input_buffer, '\n', | ||
[this](boost::system::error_code ec, std::size_t length) { | ||
if (!ec) { | ||
std::istream is(&input_buffer); | ||
std::string line; | ||
std::getline(is, line); | ||
handle_input(line); | ||
start_read(); | ||
} | ||
}); | ||
} | ||
|
||
void handle_input(const std::string& input) { | ||
std::cout << "Received input: " << input << std::endl; | ||
// Process input and update vehicle state | ||
} | ||
|
||
private: | ||
boost::asio::posix::stream_descriptor input_stream; | ||
boost::asio::streambuf input_buffer; | ||
}; | ||
|
||
int main() { | ||
boost::asio::io_context io_context; | ||
AsyncInputHandler input_handler(io_context); | ||
|
||
io_context.run(); | ||
|
||
return 0; | ||
} | ||
---- | ||
|
||
=== Develop the Rendering System | ||
|
||
If your simulation includes graphical output, develop a rendering system. This could involve using a graphics library like https://www.sfml-dev.org/index.php[SFML], https://www.libsdl.org/[SDL], or https://www.opengl.org/[OpenGL]. | ||
|
||
Convert geometric data into drawable objects for your chosen graphics library. | ||
|
||
While developing the rendering system, you might also use boost:asio[] to manage any asynchronous rendering tasks or data streams required for visual output. | ||
|
||
An advanced concept is to use boost:thread[] to parallelize the physics and rendering systems, for example: | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/thread.hpp> | ||
|
||
void physics_update() { | ||
// Perform physics calculations | ||
} | ||
|
||
void render_update() { | ||
// Perform rendering operations | ||
} | ||
|
||
int main() { | ||
boost::thread physics_thread(physics_update); | ||
boost::thread render_thread(render_update); | ||
|
||
physics_thread.join(); | ||
render_thread.join(); | ||
|
||
return 0; | ||
} | ||
---- | ||
|
||
Another advanced option is to use boost:pool[] threads to manage particle systems: | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/pool/object_pool.hpp> | ||
|
||
struct Particle { | ||
// Particle properties | ||
}; | ||
|
||
int main() { | ||
boost::object_pool<Particle> particle_pool; | ||
|
||
Particle* p = particle_pool.construct(); | ||
// Use particle | ||
|
||
particle_pool.destroy(p); | ||
|
||
return 0; | ||
} | ||
---- | ||
|
||
=== Test and Iterate | ||
|
||
Continuously test your simulation and iterate on your design. Ensure that the timing is accurate and the vehicle dynamics behave as expected. In particular, verify that all geometric calculations are correct and ensure that spatial computations are efficient, especially if you have real-time constraints. | ||
|
||
Another concern is ensuring that asynchronous operations are correctly handled and do not introduce latency or performance issues in the simulation. | ||
|
||
Perhaps utilize boost:serialization[] when saving the state of your simulation (perhaps for saving and later loading the state of the simulation), or for networked simulations where you need to transmit state information, for example: | ||
|
||
[source,cpp] | ||
---- | ||
#include <boost/serialization/serialization.hpp> | ||
#include <boost/serialization/vector.hpp> | ||
#include <boost/archive/text_oarchive.hpp> | ||
#include <boost/archive/text_iarchive.hpp> | ||
#include <fstream> | ||
|
||
struct SimulationState { | ||
std::vector<double> positions; | ||
std::vector<double> velocities; | ||
|
||
template<class Archive> | ||
void serialize(Archive& ar, const unsigned int version) { | ||
ar & positions; | ||
ar & velocities; | ||
} | ||
}; | ||
|
||
int main() { | ||
SimulationState state; | ||
state.positions.push_back(1.0); | ||
state.velocities.push_back(0.5); | ||
|
||
std::ofstream ofs("state.txt"); | ||
boost::archive::text_oarchive oa(ofs); | ||
oa << state; | ||
|
||
return 0; | ||
} | ||
---- | ||
|
||
=== Run! | ||
|
||
Real-time simulations are rarely complete because of the near-infinite complexity of the real world - there is always more that could be added. However, integrating the libraries mentioned above at appropriate stages of your development can help you create a robust, efficient, scalable and potentially wonderful simulation. | ||
|
||
== See Also | ||
|
||
* xref:resources.adoc[] | ||
* xref:testing-debugging.adoc[] | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we should keep talking about libraries like Boost.Thread in these contexts where we describe what makes Boost unique. This functionality has been available as
std::thread
in the standard library for 13 years, and I don't think Boost.Thread has received much maintenance or new features since then.