Skip to content

Commit

Permalink
Working version
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzodellagiustina committed Sep 10, 2024
1 parent 4d110a9 commit dada87b
Show file tree
Hide file tree
Showing 10 changed files with 363 additions and 75 deletions.
34 changes: 34 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ tokio-util = "0.7.11"
# Utils
anyhow = "=1.0.86"
struct_iterable = "0.1.1"
csv = "1.3.0"

# Derive
fsds-rs-derive = { path = "./fsds-rs-derive" }
33 changes: 33 additions & 0 deletions examples/control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use fsds_rs::{client, types::CarControls};
use std::{thread::sleep, time::Duration};

/// The name of the vehicle to control.
const VEHICLE_NAME: &str = "FSCar";

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
// ---------- //
// CONNECTION //
// ---------- //
// Connect to the simulator.
let mut client = client::FSDSClient::init(None, None)
.await
.expect("Cannot establish a connection with the simulator");
// Check network connection, exit if not connected.
client.ping().await?;
// Enable control of the vehicle via the API.
client.enable_api_control(VEHICLE_NAME).await?;

// ---------------- //
// CONTROL THE CAR! //
// ---------------- //
// Set the throttle to 1.0.
let mut controls = CarControls::default();
controls.throttle = 1.0;
client.set_car_controls(controls, VEHICLE_NAME).await;

// Loop to keep the program running.
loop {
sleep(Duration::from_secs(1));
}
}
131 changes: 131 additions & 0 deletions examples/gather_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::{fs::File, str::FromStr};

use fsds_rs::{
client,
types::{KinematicsState, Vector3r},
};
use msgpack_rpc::Value;

/// The name of the vehicle to control.
const VEHICLE_NAME: &str = "FSCar";
/// The path to the CSV file containing the ground truth cones.
const CSV_PATH: &str =
"../Formula-Student-Driverless-Simulator/maps/FormulaElectricBelgium/track_droneport.csv";

enum Class {
Vehicle,
ConeYellow,
ConeBlue,
ConeBigOrange,
}

impl FromStr for Class {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"yellow" => Ok(Class::ConeYellow),
"blue" => Ok(Class::ConeBlue),
"big_orange" => Ok(Class::ConeBigOrange),
_ => Err(format!("Unknown class: {}", s)),
}
}
}

/// An object in the 3D space.
/// Distances in meters.
struct Object {
vector: Vector3r,
class: Class,
}

impl Object {
fn new(x_val: f64, y_val: f64, z_val: f64, class: Class) -> Self {
Self {
vector: Vector3r {
x_val,
y_val,
z_val,
},
class,
}
}

fn from_vector3r(vector: Vector3r, class: Class) -> Self {
Self { vector, class }
}
}

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
// ---------- //
// CONNECTION //
// ---------- //
// Connect to the simulator.
let mut client = client::FSDSClient::init(None, None)
.await
.expect("Cannot establish a connection with the simulator");
// Check network connection, exit if not connected.
client.ping().await?;

// Get cones ground truth from CSV file.
let mut cones_gt = Vec::new();
// Open and read the CSV file.
let csv_file = File::open(CSV_PATH)?;
let mut csv_reader = csv::Reader::from_reader(csv_file);
for row in csv_reader.records() {
// Create a dictionary for each row.
let row = row?;
let entry = Object::new(
row[1].parse().unwrap(),
row[2].parse().unwrap(),
0.0, // The cones are on the ground.
row[0].parse().unwrap(),
);
// Append the dictionary to the list.
cones_gt.push(entry);
}

// --------- //
// MAIN LOOP //
// --------- //
let mut instant = std::time::Instant::now();
let mut fps = 0;
loop {
// Get the ground truth kinematics of the vehicle.
let kinematics_gt: KinematicsState = client
.sim_get_ground_truth_kinematics(VEHICLE_NAME)
.await?
.try_into()
.unwrap();
let car_position = kinematics_gt.position;
let car_orientation = kinematics_gt.orientation;

let _relative_cones_gt = cones_gt
.iter()
.map(|cone| cone.vector - car_position)
.collect::<Vec<_>>();

// Get onboard image.
let image = client
.sim_get_image("cam1", fsds_rs::types::ImageType::Scene, VEHICLE_NAME)
.await?;

if let Value::Binary(_image) = image {
// Save the image to a file.
//let mut file = File::create("image.png")?;
//file.write_all(&image)?;

// Do something with the image and the cones.

// Calculate the FPS.
if instant.elapsed().as_secs() >= 2 {
println!("FPS: {}", fps / 2);
fps = 0;
instant = std::time::Instant::now();
} else {
fps += 1;
}
}
}
}
27 changes: 0 additions & 27 deletions examples/ping.rs

This file was deleted.

70 changes: 65 additions & 5 deletions fsds-rs-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, FieldsNamed};

/// Implements From for Value for an iterable struct.
#[proc_macro_derive(IntoValue)]
pub fn from_for_value_derive(input: TokenStream) -> TokenStream {
#[proc_macro_derive(FromIntoValue)]
pub fn from_and_into_for_value_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;

let expanded = quote! {
let from_impl = quote! {
impl From<#name> for Value {
fn from(value: #name) -> Self {
let vec = value
.iter()
.map(|(k, v)| {
v.type_id();
(k.into(), any_to_value(v))
})
.collect();
Expand All @@ -26,5 +25,66 @@ pub fn from_for_value_derive(input: TokenStream) -> TokenStream {
}
};

let fields = if let Data::Struct(DataStruct { fields: Fields::Named(FieldsNamed { named, .. }), .. }) = &input.data {
named
} else {
// Works only for structs with named fields.
unimplemented!();
};

let try_from_impl = {

let fields_def = fields.iter().map(|field| {
let field_name = &field.ident;
quote! {
let pos = map.iter().position(|(k, _)| k
.as_str()
.unwrap_or("") // TODO: throw instead this error for better
// debugging: Value::Map should be Vec<(String, _)> to be converted to a struct, but {} was found as a key
== stringify!(#field_name)
).ok_or(anyhow::anyhow!("Field {} not found in Value::Map.", stringify!(#field_name)))?;
let #field_name = map
.remove(pos)
.1
.try_into()
.map_err(|_| anyhow::anyhow!("Every field of {} should be convertible to Value.", stringify!(#name)))?;
}
});

let fields = fields.iter().map(|field| {
let field_name = &field.ident;
quote! {
#field_name
}
});

quote! {
impl TryFrom<Value> for #name {
type Error = anyhow::Error;

fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Map(mut map) => {
#(#fields_def)*
if map.is_empty() {
Ok(#name {
#(#fields),*
})
} else {
Err(anyhow::anyhow!("Value::Map contains extra fields: {:?}", map))
}
}
_ => Err(anyhow::anyhow!("Value should be a Map to be converted to {}", stringify!(#name))),
}
}
}
}
};

let expanded = quote! {
#from_impl
#try_from_impl
};

TokenStream::from(expanded)
}
Binary file added image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit dada87b

Please sign in to comment.