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.
-
fmtis ocaml format pretty-printer combinator. It exposesFormatpretty-print functions. -
fmt_helpersis a function that accepsfmtandsystems -
'a ->means the function is taking in multiple "any type" inputs and output -
-> unitmeans 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
stdincompilation.- 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
Outputfunction to returnSelf::Outputtype!
- 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_inputandread_next
- Modify
-
- Replace
Result<Self::Input, String>withSelf::Input(Ditto forSelf::Outputequivalents)
- 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 ourRealvalue 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 usesstdlibrary 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
-> Selfon allnextfunctions. 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_calcfunction. - 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
Selfwhen 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
Selfused in a trait, and replaced mySelf::Arraywitharray: Self::Array, this seemed to compile. Not sure why this difference occurs in theno_stdversion. - Having an issue with using the trait function
read_initoutside 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
Inputtype.- Will have to return
Selfinstead ofResult - Remove all
Okreturns and try! statements
- Will have to return
-
Limitfunction returns two results, then Input and the Output - Have to modify the declarations in
Sysas well- The issue with removing
Inputtypes, is thatInputvaries depending on the structure.
- The issue with removing
- Branching off the previous issue, it would be preferable to keep
Inputthe same, instead, we modifyinput_ofto create outInputtype.-
Input_ofusesstd::parseto convert our input string to aReal, we are not going to feed string values, so we don't have a need for parse. -
Input_ofgets 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
Sysuse 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
vecwith thecollectionscrate, 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_inputandread_next - Replace
Result<Self::Input, String>withSelf::Input(Ditto forSelf::Outputequivalents) - 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 ourRealvalue 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 usesstdlibrary that we want to avoid - Remove
InputReader, usesstd - Remove
help()anderror <>, usesstd
- Replace
Inputtype withArraytype, 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
initto create theComputestruct 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
Inputtype.
-
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 stringNone => "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 amatchto distinguish between the Ok or the Err. -
try! Helper macro for unwrapping
Resultvalues, 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
-
mainrunsclap_and_run() -
clap_and_run()checks for command line args like--helpor--limit, etc. to run individual functions. -
limitseems 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():InputReaderdata, separates out the values in the commas, checks for "exit" or "quit", places values in avec, returns thevec -
read_init(): runsread_inputto get vec, then runs::initon the inputs to get initial state -
read_next(): runsself.nexton 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 anInputtype input, Input type is created usinginput_of -
impl, implement are trait calledSys, which has various types and functions.-
pub traitsimply gives a basic overview of the functions and types -
impl Sys for xxxxxxdescribes details specific to each class.
-
- Create
- Run
self::initto initialize PID and run 1st calculation. - Run
self.nexteach time a view value needs to be computed. - Compute init will call Compute_calc init.
- Preferrable we modify
initandnextto be used without aSelf::Inputinput, but with a function input.
-
cargo build --binsto build binaries,./target/debug/Compute_implemto run the binary, use --XXXX to run specific function. -
Compute()runsCompute_calcto 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
stdinstdouttransactions.
- 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.