Skip to content

louisdezons/CRocketProject

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rocket Control Project

The goal of this project is to build navigation algorithm for a 2D rocket navigating an asteroid field.

You will write two main C functions:

  • a path generator that returns a collision-free waypoint path
  • a controller that steers the rocket along that path

The Python simulator loads your C code, displays the path you generate, and shows how the rocket behaves in motion.

What You Need To Edit

You should work mainly in these files:

The simulator expects these exact function names:

void generate_trajectory(const PlanningProblem *problem, Trajectory *trajectory);
void controller_step(const ControlInput *input, ControlOutput *output);

Your Goal

Your work has two parts:

  1. Generate a path from the start position to the goal while avoiding obstacles.
  2. Control the rocket so it follows the path and reaches the goal without collision.

In practice:

  • generate_trajectory() chooses where the rocket should go
  • controller_step() chooses how the engines should fire

Quick Start

Required tools:

  • Python 3
  • make
  • a C compiler such as cc or gcc

Setup and build:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
make src

This builds the shared library loaded by the simulator:

src/librocket.so

Rebuild with make src every time you change your C code.

How To Run The Simulator

See The Path You Generated

Use plan mode when you want to inspect your path generator:

python -m sim.run --map moving_gate --mode plan --show-safety --show-prediction

Plan mode is the best place to debug:

  • the waypoint path you returned
  • the planner graph you returned
  • whether your path goes through obstacles
  • whether your path is too long, too sharp, or badly placed

See How The Rocket Behaves

Use flight mode when you want to inspect your controller:

python -m sim.run --map moving_gate --mode flight --show-safety --show-prediction

Flight mode lets you observe:

  • whether the rocket turns toward the path correctly
  • whether it accelerates too much or too little
  • whether it overshoots waypoints
  • whether it collides with obstacles or walls
  • whether your controller remains stable over time

In flight mode, the simulator now calls generate_trajectory() at every simulation step before controller_step(). The Python side no longer decides when to replan. If you want to keep a previous graph or path, do it inside your C code.

Useful Workflow

A good way to go through this project is:

  1. Start with plan mode and make sure your path looks reasonable.
  2. Then switch to flight mode and see whether the rocket can track that path.
  3. Rebuild with make src after every edit.
  4. Improve the planner and controller together.

What The Simulator Gives You

Path Generator Input

Your path generator receives a PlanningProblem:

void generate_trajectory(const PlanningProblem *problem, Trajectory *trajectory);

Important fields in problem:

Field Meaning
width, height map dimensions
start rocket start position
goal final target position
state.x, state.y current rocket position
state.vx, state.vy current rocket velocity
state.theta current rocket heading
state.omega current rocket angular velocity
state.mass current rocket mass
start_heading initial rocket heading
start_speed initial rocket speed
cruise_speed recommended planning speed hint
rocket_radius collision radius of the rocket
obstacle_count number of valid obstacles
obstacles[i].center obstacle position
obstacles[i].radius obstacle radius
obstacles[i].velocity obstacle velocity
obstacles[i].is_moving 1 if moving, 0 if static

Important note:

  • for moving scripted obstacles, is_moving is more reliable than velocity
  • in flight mode, generate_trajectory() is called every frame, so you can decide inside trajectory_generator.c whether to rebuild the graph, update only the path, or reuse cached results

Path Generator Output

Your function must fill a Trajectory:

Field Meaning
waypoint_count number of valid points in the returned path
waypoints[i] ordered path points that the rocket should follow from start toward goal
graph_node_count number of valid nodes in the planner graph
graph_nodes[i] positions of the planner graph nodes
graph_edge_count number of valid graph edges
graph_edges[i][0..1] indices of the two endpoint nodes of each graph edge

The two parts of the output have different roles.

The waypoints array is the actual path output used by the simulator. It should be read as an ordered polyline:

  • waypoints[0] should usually be near the current rocket position or the current local planning start
  • the last waypoint should usually be near the goal
  • consecutive waypoints define the path segments the controller will try to follow

In flight mode, the simulator passes this path to controller_step(). That means the quality of the waypoint sequence matters directly: a path with unnecessary zig-zags, sharp turns, or badly spaced points may be valid geometrically but still hard to track.

The graph output is different. graph_nodes and graph_edges are mainly there to expose what your planner considered internally. You can use them to return:

  • a roadmap built by PRM
  • a search graph explored by A*
  • a tree or rewired tree built by RRT or RRT*
  • any other node-edge structure that helps explain how the path was produced

Each edge graph_edges[k] = {i, j} means that node i and node j are connected. The simulator draws this graph, so it is useful for debugging sampling coverage, graph connectivity, and search behavior. The controller does not directly use the graph; it only uses the waypoint path.

Basic rules:

  • return a valid path from start to goal
  • keep waypoints inside the map
  • avoid collisions with obstacles
  • keep counts within the fixed array limits
  • if you return graph edges, they must refer to valid graph nodes

The planner graph is displayed by the simulator, so you can use it to understand what your algorithm explored, while the waypoint list is what the rocket will actually try to track.

What The Controller Receives

Your controller receives a ControlInput:

void controller_step(const ControlInput *input, ControlOutput *output);

Important fields in input:

Field Meaning
state.x, state.y rocket position
state.vx, state.vy rocket velocity
state.theta rocket heading
state.omega angular velocity
goal final target
target_waypoint current waypoint selected by the simulator
waypoint_index index of the current waypoint
waypoint_count number of valid waypoints
waypoints[i] full path
obstacle_count number of obstacles
obstacles[i] obstacle information

Your controller must write a ControlOutput:

Field Meaning
main_throttle main engine command
left_booster left booster command
right_booster right booster command

Controller rules:

  • output finite values only
  • keep commands in the intended range [0, 1]
  • remember the controller runs at 60 Hz

Coordinate System

The simulator uses these conventions:

  • map size is 1200 x 800
  • x increases to the right
  • y increases downward
  • theta = 0 means the rocket points upward
  • distances and speeds are simulator units, not SI units

Basics For The Path Generator

Start with a planner that produces a path the controller can actually follow, not just a path that is collision-free on paper. In this project, a good path starts near the rocket, ends near the goal, stays inside the map, keeps clearance from obstacles, and avoids unnecessary zig-zags or very sharp turns.

If you want concrete families of methods to study, the most useful names are A*, visibility graphs, PRM, RRT, RRT*, and Hybrid A*. For this simulator, the most practical solutions are often a graph-based planner such as A* or a sampling-based planner such as RRT*, with a cost that balances path length and obstacle clearance.

When you inspect plan mode, focus on just a few questions:

  • Does the path begin near start and end near goal?
  • Is every segment collision-free with enough margin for the rocket radius?
  • Is the path smooth enough that the controller can turn and follow it?

Basics For The Controller

The controller should first make the rocket point in the right direction, then add thrust once the attitude is acceptable. In other words, you are controlling both heading and forward speed, while trying to avoid oscillations and overshoot. A path that looks good in plan mode may still fail in flight mode if the controller asks for too much thrust while the rocket is misaligned.

Useful controller names to know are PID, PD, Pure Pursuit, Stanley, LQR, and MPC. A strong first implementation for this assignment is usually much simpler than full MPC: you can get good results from a waypoint-following controller built from heading error, angular-rate damping, and thrust reduction when alignment is poor.

When you inspect flight mode, focus on these points:

  • Does the rocket turn toward the waypoint before accelerating?
  • Does it oscillate, overshoot, or miss waypoints at high speed?
  • Does it remain stable when the path bends or when obstacles force rerouting?

Rocket Dynamics You Should Know

The controller is easier to write if you understand the physics in sim/physics.py.

Constants:

Property Value
collision radius 18
mass 1.0
max force per engine 240
translational force gain 0.9
linear damping 0.11
torque gain 0.135
angular damping 0.72
goal capture radius 40

With time step $\Delta t = \frac{1}{60}$, the simulator uses:

$$ F_{\mathrm{main}} = 240,u_{\mathrm{main}}, \qquad F_{\mathrm{left}} = 240,u_{\mathrm{left}}, \qquad F_{\mathrm{right}} = 240,u_{\mathrm{right}} $$

$$ F_{\mathrm{total}} = F_{\mathrm{main}} + F_{\mathrm{left}} + F_{\mathrm{right}} $$

$$ a_x = 0.9,F_{\mathrm{total}}\sin(\theta) - 0.11,v_x $$

$$ a_y = -0.9,F_{\mathrm{total}}\cos(\theta) - 0.11,v_y $$

$$ \tau = 0.135\left(F_{\mathrm{left}} - F_{\mathrm{right}}\right) $$

$$ \dot{\omega} = \tau - 0.72,\omega $$

$$ \omega_{k+1} = \omega_k + \dot{\omega},\Delta t, \qquad \theta_{k+1} = \theta_k + \omega_{k+1},\Delta t $$

$$ v_{x,k+1} = v_{x,k} + a_x,\Delta t, \qquad v_{y,k+1} = v_{y,k} + a_y,\Delta t $$

$$ x_{k+1} = x_k + v_{x,k+1},\Delta t, \qquad y_{k+1} = y_k + v_{y,k+1},\Delta t $$

Array Limits

These limits are defined in include/rocket_api.h:

Symbol Value
MAX_OBSTACLES 128
MAX_WAYPOINTS 256
MAX_GRAPH_NODES 320
MAX_GRAPH_EDGES 2048

Make sure your code never writes past these limits.

Recommended First Steps

To start this project a good task ordering is:

  1. In src/trajectory_generator.c, return a very simple path.
  2. Run plan mode and confirm the path is visible.
  3. Improve the path so it avoids obstacles.
  4. In src/controller.c, build a simple waypoint-following controller.
  5. Run flight mode and watch how the rocket behaves.
  6. Tune the planner and controller together.

Main Commands

Environments

The simulator provides the following environments, listed from easiest to hardest:

  1. empty_debug: empty map for debugging controller alignment, turning, and thrust without obstacles.
  2. static_intro: first static planning map with a few large asteroids.
  3. narrow_passage: static map with bottlenecks that require a cleaner path.
  4. moving_gate: dynamic map with a timed moving opening.
  5. patrol_crossing: hardest environment, with several moving obstacles and crossing patrol patterns.

Build:

make src

Show the generated path:

python -m sim.run --map empty_debug --mode plan --show-safety --show-prediction

Show the rocket behavior in flight:

python -m sim.run --map empty_debug --mode flight --show-safety --show-prediction

Try a static obstacle map:

python -m sim.run --map static_intro --mode flight --show-safety --show-prediction

Try the hardest dynamic map:

python -m sim.run --map patrol_crossing --mode flight --show-safety --show-prediction

About

Implement a path generator and controller for a 2D rocket in an asteroid field

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors