franka_interface/
experiment.rs

1// Copyright (c) 2021 Marco Boneberger
2// Licensed under the EUPL-1.2-or-later
3//! Contains the Experiment struct, which is needed to spawn and connect to robots
4
5use crate::environment::{FrankaEnvironment, FrankaRealEnvironment, FrankaSimEnvironment};
6use crate::{FrankaReal, FrankaSim, FrankaSimWithoutClient, Robot, RobotArguments};
7use rubullet::image::RgbaImage;
8use rubullet::PhysicsClient;
9use std::cell::{RefCell, RefMut};
10use std::path::PathBuf;
11use std::rc::Rc;
12use std::time::Duration;
13
14/// choose whether to run your [`Experiment`](`Experiment`) in the simulation or with the real robot
15pub enum Mode {
16    /// Runs the experiment in the simulation.
17    Simulation,
18    /// Runs the experiment on the real robot.
19    Real,
20}
21/// A trait which contains methods for handling the simulation. Apart from
22/// [`set_franka_urdf_path`](`Self::set_franka_urdf_path`),
23/// all methods have a default implementation,
24/// so you do not have to implement them if you do not need them.
25pub trait SimulationSetup {
26    /// Sets how often the simulation should be updated inside the control loop. The default is
27    /// 1/240s  = 240 Hz
28    fn set_simulation_time_step(&self) -> Duration {
29        Duration::from_secs_f64(1. / 240.)
30    }
31    /// Return the path to the URDF file of the Franka robot.
32    fn set_franka_urdf_path(&self) -> PathBuf;
33    /// this method runs directly after the simulation is spawned. Use this method for example
34    /// to load additional object into the simulation.
35    fn setup_simulation(&mut self, _client: Rc<RefCell<PhysicsClient>>) {}
36    /// this method is intended to let you setup the camera. It is run directly after setup_simulation.
37    fn setup_camera(&mut self, _client: Rc<RefCell<PhysicsClient>>) {}
38    /// determine how you want to get the camera image with this method.
39    fn get_camera_image(&mut self, _client: Rc<RefCell<PhysicsClient>>) -> RgbaImage {
40        RgbaImage::default()
41    }
42}
43/// A trait which contains methods for handling the real robot. All methods have a default implementation,
44/// so you do not have to implement them if you do not need them.
45pub trait RealSetup {
46    /// This method runs directly on calling [`Experiment::new`](`Experiment::new`). You can use
47    /// it to run specific code which is only needed when using the real hardware.
48    fn setup_real(&mut self) {}
49    /// Runs directly after [`setup_real`](`Self::setup_real`) and is intended for setting up the camera.
50    fn setup_camera(&mut self) {}
51    /// determine how you want to get the camera image with this method.
52    fn get_camera_image(&mut self) -> RgbaImage {
53        RgbaImage::default()
54    }
55}
56/// Use it to create a new experiment which can either run on the real robot or in the simulation.
57///
58/// Use [`new`](`Self::new`) to create an experiment and then use [`new_robot`](`Self::new_robot`) to
59/// create a new robot.
60///
61pub struct Experiment<Sim: ?Sized + SimulationSetup, Real: ?Sized + RealSetup> {
62    pub environment: FrankaEnvironment,
63    pub sim_setup: Box<Sim>,
64    pub real_setup: Box<Real>,
65}
66
67impl Experiment<dyn SimulationSetup, dyn RealSetup> {
68    /// Use it to create a new experiment which can either run on the real robot or in the simulation
69    ///
70    /// # Arguments
71    /// * `mode` - specify whether to run the experiment in simulation or with a real robot
72    /// * `sim_setup` - something that implements [`SimulationSetup`](`SimulationSetup`).
73    /// * `real_setup` - something that implements [`RealSetup`](`RealSetup`).
74    ///
75    /// # Example
76    /// ```no_run
77    /// use franka_interface::experiment::{Experiment, Mode, RealSetup, SimulationSetup};
78    /// use franka_interface::types::Vector7;
79    /// use franka_interface::RobotArguments;
80    /// use std::f64::consts::PI;
81    /// use std::path::PathBuf;
82    /// struct MySimSetup {}
83    /// impl SimulationSetup for MySimSetup {
84    ///     fn set_franka_urdf_path(&self) -> PathBuf {
85    ///         "path/to/panda.urdf".into()
86    ///     }
87    /// }
88    /// struct MyRealSetup {}
89    /// impl RealSetup for MyRealSetup {}
90    /// let mut env = Experiment::new(Mode::Simulation, MySimSetup {}, MyRealSetup {});
91    /// let mut robot = env.new_robot(RobotArguments {
92    ///     hostname: "franka".to_string(),
93    ///     base_pose: None,
94    ///     initial_config: None,
95    /// });
96    /// robot.joint_motion(
97    ///     0.1,
98    ///     Vector7::from_column_slice(&[1., PI / 4., 0., -2. * PI / 4., 0., PI / 2., -PI / 4.]),
99    /// );
100    /// println!("{:?}", robot.get_state());
101    /// ```
102    pub fn new(
103        mode: Mode,
104        mut sim_setup: impl SimulationSetup + 'static,
105        mut real_setup: impl RealSetup + 'static,
106    ) -> Experiment<dyn SimulationSetup, dyn RealSetup> {
107        match mode {
108            Mode::Simulation => {
109                let environment = Box::new(FrankaSimEnvironment::new(
110                    sim_setup.set_simulation_time_step(),
111                ));
112
113                sim_setup.setup_simulation(environment.client.clone());
114                sim_setup.setup_camera(environment.client.clone());
115                let environment = FrankaEnvironment::Simulation(environment);
116                Experiment {
117                    environment,
118                    sim_setup: Box::new(sim_setup),
119                    real_setup: Box::new(real_setup),
120                }
121            }
122            Mode::Real => {
123                let environment = FrankaEnvironment::Real(Box::new(FrankaRealEnvironment::new()));
124                real_setup.setup_real();
125                real_setup.setup_camera();
126                Experiment {
127                    environment,
128                    sim_setup: Box::new(sim_setup),
129                    real_setup: Box::new(real_setup),
130                }
131            }
132        }
133    }
134    /// returns the current image from the robot. Make sure you implemented `get_camera_image`
135    /// in your [`RealSetup`](`RealSetup`) and [`SimulationSetup`](`SimulationSetup`).
136    pub fn get_image(&mut self) -> RgbaImage {
137        match &self.environment {
138            FrankaEnvironment::Real(_) => self.real_setup.get_camera_image(),
139            FrankaEnvironment::Simulation(environment) => {
140                self.sim_setup.get_camera_image(environment.client.clone())
141            }
142        }
143    }
144
145    /// spawns/connects to a new robot using the [`RobotArguments`](`crate::RobotArguments`)
146    pub fn new_robot(&mut self, config: RobotArguments) -> Robot {
147        match &mut self.environment {
148            FrankaEnvironment::Real(_environment) => {
149                Robot::Real(FrankaReal::new(config.hostname.as_str()))
150            }
151            FrankaEnvironment::Simulation(environment) => {
152                let args = FrankaSimWithoutClient {
153                    urdf_path: self.sim_setup.set_franka_urdf_path(),
154                    base_pose: config.base_pose,
155                    initial_config: config.initial_config,
156                };
157                Robot::Sim(FrankaSim::new(
158                    environment.client.clone(),
159                    args,
160                    &environment.time_step,
161                ))
162            }
163        }
164    }
165    /// Query whether the current experiment is run in simulation
166    pub fn is_simulation(&self) -> bool {
167        match self.environment {
168            FrankaEnvironment::Simulation(_) => true,
169            FrankaEnvironment::Real(_) => false,
170        }
171    }
172    /// allows accessing the PhysicsClient in Simulation by defining a closure that takes
173    /// the PhysicsClient as input.
174    /// Nothing will happen if the experiment is not run in the simulation
175    /// # Return
176    /// * In the simulation it will return Some(T) where T is the return type of the client_cb
177    /// * Will return None when not executed with the simulation.
178    pub fn use_physics_client<F: FnMut(RefMut<PhysicsClient>) -> T, T>(
179        &mut self,
180        mut client_cb: F,
181    ) -> Option<T> {
182        match &self.environment {
183            FrankaEnvironment::Simulation(sim) => Some(client_cb(sim.client.borrow_mut())),
184            FrankaEnvironment::Real(_) => None,
185        }
186    }
187}