|
1 | 1 | use dialoguer::{theme::ColorfulTheme, Select}; |
2 | 2 | use nalgebra::{Const, Matrix1, Matrix4, Matrix4x1, Vector4}; |
3 | | -use plotpy::{Curve, Plot}; |
| 3 | +use plotters::prelude::*; |
4 | 4 | use std::error::Error; |
5 | 5 |
|
6 | 6 | extern crate robotics; |
@@ -88,55 +88,87 @@ fn main() -> Result<(), Box<dyn Error>> { |
88 | 88 | // let algo = algos[algo_idx]; |
89 | 89 |
|
90 | 90 | // get data |
91 | | - let (states, commands) = run().expect("unable to run"); |
| 91 | + let (states, _commands) = run().expect("unable to run"); |
92 | 92 |
|
93 | 93 | // Create output directory if it didnt exist |
94 | 94 | std::fs::create_dir_all("./img")?; |
95 | 95 |
|
96 | | - let path = match algo_idx { |
| 96 | + let path_prefix = match algo_idx { |
97 | 97 | 0 => "lqr", |
98 | 98 | 1 => "mpc", |
99 | 99 | _ => unreachable!(), |
100 | 100 | }; |
101 | 101 |
|
102 | | - let time = (0..states.len()) |
103 | | - .map(|i| (i as f64) / (states.len() as f64) * 5.0) |
104 | | - .collect::<Vec<f64>>(); |
105 | | - |
106 | | - let mut curve_x = Curve::new(); |
107 | | - curve_x |
108 | | - .set_label("x") |
109 | | - .draw(&time, &states.iter().map(|s| s.x).collect()); |
110 | | - |
111 | | - let mut curve_theta = Curve::new(); |
112 | | - curve_theta |
113 | | - .set_label("theta") |
114 | | - .draw(&time, &states.iter().map(|s| s.z).collect()); |
115 | | - |
116 | | - let mut curve_x_dot = Curve::new(); |
117 | | - curve_x_dot |
118 | | - .set_label("x dot") |
119 | | - .draw(&time, &states.iter().map(|s| s.y).collect()); |
120 | | - |
121 | | - let mut curve_theta_dot = Curve::new(); |
122 | | - curve_theta_dot |
123 | | - .set_label("theta dot") |
124 | | - .draw(&time, &states.iter().map(|s| s.w).collect()); |
125 | | - |
126 | | - let mut curve_u = Curve::new(); |
127 | | - curve_u.set_label("u").draw(&time, &commands); |
128 | | - |
129 | | - // add features to plot |
130 | | - let mut plot = Plot::new(); |
131 | | - |
132 | | - plot.add(&curve_x) |
133 | | - .add(&curve_theta) |
134 | | - .add(&curve_x_dot) |
135 | | - .add(&curve_theta_dot) |
136 | | - // .add(&curve_u) |
137 | | - .legend() |
138 | | - .set_equal_axes(true) // save figure |
139 | | - .set_figure_size_points(1000.0, 1000.0) |
140 | | - .save(format!("img/{}-{}.svg", path, "done").as_str())?; |
| 102 | + let full_path = format!("img/{}-{}.svg", path_prefix, "done"); |
| 103 | + |
| 104 | + let root = SVGBackend::new(&full_path, (1000, 1000)).into_drawing_area(); |
| 105 | + root.fill(&WHITE)?; |
| 106 | + |
| 107 | + let time: Vec<f64> = (0..states.len()) |
| 108 | + .map(|i| (i as f64) * 0.01) // dt = 0.01 |
| 109 | + .collect(); |
| 110 | + |
| 111 | + let mut min_y = states.iter().map(|s| s.min()).fold(f64::INFINITY, f64::min); |
| 112 | + let mut max_y = states |
| 113 | + .iter() |
| 114 | + .map(|s| s.max()) |
| 115 | + .fold(f64::NEG_INFINITY, f64::max); |
| 116 | + |
| 117 | + min_y -= 0.1; |
| 118 | + max_y += 0.1; |
| 119 | + |
| 120 | + let mut chart = ChartBuilder::on(&root) |
| 121 | + .caption( |
| 122 | + format!("Inverted Pendulum - {}", algos[algo_idx]), |
| 123 | + ("sans-serif", 40), |
| 124 | + ) |
| 125 | + .margin(20) |
| 126 | + .x_label_area_size(40) |
| 127 | + .y_label_area_size(40) |
| 128 | + .build_cartesian_2d(0f64..5f64, min_y..max_y)?; |
| 129 | + |
| 130 | + chart |
| 131 | + .configure_mesh() |
| 132 | + .x_desc("Time [s]") |
| 133 | + .y_desc("State Value") |
| 134 | + .draw()?; |
| 135 | + |
| 136 | + let state_configs = vec![ |
| 137 | + (states.iter().map(|s| s.x).collect::<Vec<_>>(), &BLUE, "x"), |
| 138 | + ( |
| 139 | + states.iter().map(|s| s.y).collect::<Vec<_>>(), |
| 140 | + &CYAN, |
| 141 | + "x dot", |
| 142 | + ), |
| 143 | + ( |
| 144 | + states.iter().map(|s| s.z).collect::<Vec<_>>(), |
| 145 | + &RED, |
| 146 | + "theta", |
| 147 | + ), |
| 148 | + ( |
| 149 | + states.iter().map(|s| s.w).collect::<Vec<_>>(), |
| 150 | + &GREEN, |
| 151 | + "theta dot", |
| 152 | + ), |
| 153 | + ]; |
| 154 | + |
| 155 | + for (data, color, label) in state_configs { |
| 156 | + chart |
| 157 | + .draw_series(LineSeries::new( |
| 158 | + time.iter().zip(data.iter()).map(|(&t, &v)| (t, v)), |
| 159 | + color.stroke_width(2), |
| 160 | + ))? |
| 161 | + .label(label) |
| 162 | + .legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], color.clone())); |
| 163 | + } |
| 164 | + |
| 165 | + chart |
| 166 | + .configure_series_labels() |
| 167 | + .background_style(&WHITE.mix(0.8)) |
| 168 | + .border_style(&BLACK) |
| 169 | + .draw()?; |
| 170 | + |
| 171 | + root.present()?; |
| 172 | + |
141 | 173 | Ok(()) |
142 | 174 | } |
0 commit comments