Documentation AK CONTINUOUS - GaloisInc/HighAssuranceControllerOfSelfBalancingRobotCapstone GitHub Wiki
- Realize I didn't really to modify any of the Ocaml code for this to work.
- Commented out or deleted all of the Rust code that would prevent embedded from working.
- Compiled Kind2, compiled the Rust library with kind2, then tested it with arduino. Balancing worked properly.
- Important note: I have not messed around with assertions yet, so I'm not sure if this will work.
- I'm not sure what consequences there may be by me using a mutable reference instead of returning a new controller each time like the old generator used to do.
- Probably a good time to start testing everything and cleaning up for a final handoff.
Currently, my work outside of this repo is split up into these following repos: Artem1199/kind2, Artem1199/otis-arduino, Artem1199/otis-TCP, Artem1199/otis-server
. This was done this way because a lot of these projects are forks of other projects or simply don't fit anywhere on this repo.
-
fmt
is ocaml format pretty-printer combinator. It exposesFormat
pretty-print functions. -
fmt_helpers
is a function that accepsfmt
andsystems
-
'a ->
means the function is taking in multiple "any type" inputs and output -
-> unit
means it is returning a void.
- First modification to the string printout for
helper::
function resulted in a compile error withOcaml
Type 'a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h -> 'i -> 'j is not compatible with type unit
- Somehow I modified the string and changed it's type.
- I think I have an easy way around this. Comment out the code I don't need instead of deleting it. Should save some time on having to edit all these strings. Avoid dealing with Ocaml as much as possible.
- Ocaml function calls are different In general the rule is: "bracket around the whole function call — don't put brackets around the arguments to a function call"
- This is how you define a function
let average a b = (a +. b) /. 2.0;;
- Essentially avoid parenthesis. The function starts after the
=
. - OCaml doesn't have a return keyword — the last expression in a function becomes the result of the function automatically.
- Modifying the Lustre generator file. This modification will occur on my fork on Kind2: lustreToRust
- A nice feature would be to add a way call either the embedded Rust compile or the normal
stdin
compilation.- Could implement this in Flags.ml. Then modify postAnalysis.ml deal with the flag. Stretch goal if time allows.
- Noticed 1 restriction is that my output if a f64. This could be a problem if we want something besides f64 output. Or if we have multiple outputs.
- Found very easy solution to this. Simply use the already created
Output
function to returnSelf::Output
type!
- Found very easy solution to this. Simply use the already created
- Cheatsheet from last modification:
-
- Modify input_of to accept
(array: Self::Array)
instead ofvec<String>
-done
- Modify input_of to accept
-
- Modify
read_input
andread_next
- Modify
-
- Replace
Result<Self::Input, String>
withSelf::Input
(Ditto forSelf::Output
equivalents)
- Replace
-
- Remove
fn output_str
, this outputs a string for command-line examples, don't need this.
- Remove
-
-
Create
fn output_flt
, this is necessary to return ourReal
value for use by C++
-
Create
-
- Remove
fn run
, this creates a loop, we don't need this since Arduino/C++ will provide the looping
- Remove
-
- Remove or replace all
///
documentation
- Remove or replace all
-
- Remove
clamp_and_run
- Remove
-
- Remove
parse
, this usesstd
library that we want to avoid
- Remove
-
- Remove
InputReader
, usesstd
- Remove
-
- Remove
help()
anderror <>
, usesstd
- Remove
- I found a way to compile the kind2 program with my modifications. Running:
ocamlbuild -j 0 -pkg yojson -cflags -w,@P@U@F kind2.native
Inside the kind2/src
folder will build kind2 without building the other OCaml components. Problem is now I have to learn how to program Ocaml
- Can't seem to call the read_next function outside of the trait.
- The issue is that I want to input a mutable reference, but an I can't because everything inside the trait and all the implementations is not mutable.
- Considered modifying everything to have a mutable reference for FFI, and ignore it with the embedded compiler.
- Creating InputReader, creates a type that reads from the input cmd line input stream.
- The program waits for Enter to be pressed at read_line.
- Making this FFI compatible is a nightmare..
- We have to pass in a mutable reference to the memory location, but lus generated code does not work that way.
- Attempt to return all new states that are returned with .next. This was it just modifies the 1 struct instead of overriding the old struct each time.
- Having a hard time understanding the mutable reference, here's a definition `Using the mutable reference means you're referencing the place in memory where the variable x is stored. Across function boundaries, you're able to change the value stored in memory there.
- Removed the
-> Self
on allnext
functions. This way nothing is returned. Only the struct is modified, means we don't have to copy anything. Got the library to compile and ran it on the arduino. - Noticed I wrote the original lustre program PID wrong and have to fix it in Rust code. This is mostly to test functionality, not for compiler modification.
- Fixing Lustre generated PID:'
- I believe I only really have to modify my 1
comptue_calc
function. - Corrected the 1 function, and had to correct array inputs because arrays also needs Rust/C++ FFI.
- Outputs of old PID and this new lustre generated PID are equal.
- I believe I only really have to modify my 1
- Robot is balancing itself with the modified lustre PID!!
- Next step modifying Lustre generator.
Try removing the return on Self.
- Issues with
Self
when used as input, error forexpected one of 9 possible tokens
, whileSelf::
on output works fine. - A lot of programmers use Rust generics instead of types for traits in embedded Rust.
- Generics allow users to share the same function for multiple types.
- Found an example of
Self
used in a trait, and replaced mySelf::Array
witharray: Self::Array
, this seemed to compile. Not sure why this difference occurs in theno_std
version. - Having an issue with using the trait function
read_init
outside of the trait.
- New robot can self balance with Rust PID.
- Removed the additional YAW pid to simplify things for now. May need to reintroduce in the future.
- There is an issue with setpoint slightly changing after not using the robot for a while. Not sure how to deal with that atm.
- Some references; Great video for PID calibration, Another video on uses for the encoders + kalman filter, also consider impacts of battery drain.
- Modify the init method construct to create a Compute based on direct inputs instead of the
Input
type.- Will have to return
Self
instead ofResult
- Remove all
Ok
returns and try! statements
- Will have to return
-
Limit
function returns two results, then Input and the Output - Have to modify the declarations in
Sys
as well- The issue with removing
Input
types, is thatInput
varies depending on the structure.
- The issue with removing
- Branching off the previous issue, it would be preferable to keep
Input
the same, instead, we modifyinput_of
to create outInput
type.-
Input_of
usesstd::parse
to convert our input string to aReal
, we are not going to feed string values, so we don't have a need for parse. -
Input_of
gets fed a vec from read_inputs that converts our command line input string to a vec - The whole program is structured to have the trait of
Sys
, which is the same for every function and node. - This creates an issue because we want a uniquely sized input for every single function. We also can't really use a Vec input because C++ needs a specific size to allocate memory for.
- If we assume the parent function will have the largest number of inputs, then we can have the
Sys
use an array as large as the largest input. I don't believe that assumption is always true, internal functions could take something like constants that would not be input in the parent function. - In C++ we would also have to allocate memory for every node or function's struct. Whenever I call a method of a struct I have to also supply the reference to the struct. This is pretty standard for most of the Rust functions generated by Kind2, so no need to worry.
- We want to have a set size if we want this to work with C++. We can use
vec
with thecollections
crate, and a global allocator, but that doesn't seem reliable. I don't believe Rust is allowed to allocate memory when Arduino is the main program. - One alternative is to introduce shim functions outside of the Kind2 compiler that is meant to interface with C++.
- This way C++ allocates memory for a vec, then have Rust populate that vector and feeds it into a function that calls
input_of
. Example of C++, Rust vector gibberish.
- This way C++ allocates memory for a vec, then have Rust populate that vector and feeds it into a function that calls
-
- I don't think there is a way of doing this, and keeping Sys trait the same everywhere. What if we had an array of size
arity()
- The input array size can be a value of the struct itself! Meaning we can keep Sys intact! :D
- Order of run is: InputReader -> read_init -> Self::input_of -> Self::init -> loop (read_next -> Self::input_of)
- Modify input_of to accept
[Real; svar_arraySize: usize]
instead ofvec<String>
- Modify
read_input
andread_next
- Replace
Result<Self::Input, String>
withSelf::Input
(Ditto forSelf::Output
equivalents) - Remove
fn output_str
, this outputs a string for command-line examples, don't need this. -
Create
fn output_flt
, this is necessary to return ourReal
value for use by C++ - Remove
fn run
, this creates a loop, we don't need this since Arduino/C++ will provide the looping - Remove or replace all
///
documentation - Remove
clamp_and_run
- Remove
parse
, this usesstd
library that we want to avoid - Remove
InputReader
, usesstd
- Remove
help()
anderror <>
, usesstd
- Replace
Input
type withArray
type, and removeinput_of
- Will need to add shim functions for read_init and read_next
- Is it possible to make this a trait?
- The robot is working pretty well, but now I have to figure out a way to get the PID to interact with the motors properly.
- Worked on mounting MKR1010 board and MPU6050 board ontop of robot. Mostly wiring.
- Hotglue isn't good enough to hold the robot. Robot fell apart when trying to move batteries. Hotglued again for now.
- Adafruit motor shield required 4 connections, SCL, SDA, 5V, and GND. (For some reason it won't work w/o the 5v). The default I2C address is 0x60.
- Will have to focus on completing robot and work in Lustre->Rust conversion later.
- So I want to hijack
init
to create theCompute
struct and initialize it, then return the struct.- I don't care about a result, because Arduino does can't do anything with it.
- Use (1.0,2.0,...,7.0) to create an equivalent
Input
type.
-
Rust Result is a richer version of options, which has 2 options either Ok(T), an element T was found, or Err(E), an error was found.
- Enums, Options: Options are either Some or None. This can be dealt with match for example
Some(c) => c.to_string(), //if something found, then convert c to string
None => "Error not found".to_string() //Don't find it, then have an alternative response.
- So returning
Result<Self, String>
returns either the struct itself if everything is OK or an error string with an error message if there was a failure. Similar to LabVIEW passing through an error. With the result you have to use amatch
to distinguish between the Ok or the Err. -
try! Helper macro for unwrapping
Result
values, will return Err if error occurs in the function call.
- Need a way to run two separate libraries at once under 1 testbench. Each library has its own TOML file and in/out method. Lus generated PID breakdown
-
main
runsclap_and_run()
-
clap_and_run()
checks for command line args like--help
or--limit
, etc. to run individual functions. -
limit
seems to be working exactly as expected.- Anything above 1k is set to 1k, anything below -1k is set to -1k
-
run()
method of each function:- Runs
InputReader::mk()
create an InputReader - Runs read_init using the reader mut and checks for errors
- Then invokes read_next on a loop
- Runs
-
InputReader
: creates a bufferwith_capacity(100)
, and astdin()
input. -
read_input()
:InputReader
data, separates out the values in the commas, checks for "exit" or "quit", places values in avec
, returns thevec
-
read_init()
: runsread_input
to get vec, then runs::init
on the inputs to get initial state -
read_next()
: runsself.next
on inputs to get next state. -
self::init
: Creates structure with initial values, computes initial states, returns the initial state. -
self.next
: Takes input values, computes next state - Trait
Sized
, a trait that all systems must implement, including Input, Output, input_of, init, next, read_init, read next, etc.. -
Compute()
requires anInput
type input, Input type is created usinginput_of
-
impl
, implement are trait calledSys
, which has various types and functions.-
pub trait
simply gives a basic overview of the functions and types -
impl Sys for xxxxxx
describes details specific to each class.
-
- Create
- Run
self::init
to initialize PID and run 1st calculation. - Run
self.next
each time a view value needs to be computed. - Compute init will call Compute_calc init.
- Preferrable we modify
init
andnext
to be used without aSelf::Input
input, but with a function input.
-
cargo build --bins
to build binaries,./target/debug/Compute_implem
to run the binary, use --XXXX to run specific function. -
Compute()
runsCompute_calc
to account for time Sampling intervals -
limit()
limits the output between -1k to 1k - Need to implement wrap-to-pi in lustre
- Update submodules to include TCP, Webserver, and Arduino
- Continue finding way to run lus generated PID w/o
stdin
stdout
transactions.
- Using OPAM makes this process much easier.
- Ran into the following error:
fatal: No names found, cannot describe anything.
This seems like an issue related to git. Forked my own copy of Kind2, and fixed my --prefix= seemed to resolve the issue.
- Need to create a rust testbench for comparing the Lustre generated PID with the PID written in Rust.
- Rust has a few ways to write tests, first, the library is recommended to be initialized with:
cargo new NAME --lib
- Had an issue with
try!
causing rust compile fails. Removededition = "2018"
from the toml file resolved this.