VeriDroneThis project investigates the application of a technique called Foundational Verification to cyber-physical systems, paving the way to reaching previously unattainable levels of safety for cyber-physical systems. To make sure that the techniques in this project are practical, we evaluate them within the context of a real flying quadcopter.
http://ucsd-pl.github.io/veridrone/
Thu, 23 Mar 2017 04:50:42 +0000Thu, 23 Mar 2017 04:50:42 +0000Jekyll v3.4.3The Real Induction<p>We do a lot of proofs as part of this project, many of them using
induction. In computer science, we’re usually taught that induction can
prove facts about natural numbers, lists, trees, and other discrete
structures. But in VeriDrone, we deal with the physical world, which means
we work with real numbers. Can we use induction to prove facts about the
real numbers? It turns out we can, though the induction rule is more subtle
than you might expect.</p>
<h3 id="normal-induction">“Normal” Induction</h3>
<p>Let’s say you want to prove a property <script type="math/tex">P</script> of natural numbers. Induction
says that you can prove <script type="math/tex">P</script> holds on <em>all</em> natural numbers if you can
prove</p>
<ul>
<li><script type="math/tex">P</script> holds on 0 and</li>
<li>For any <script type="math/tex">n</script>, if <script type="math/tex">P</script> holds on <script type="math/tex">n</script>, then <script type="math/tex">P</script> holds on <script type="math/tex">n+1</script>.</li>
</ul>
<p>Intuitively, this process resembles dominoes. We prove a fact for (knock
over) the first element, and then use the inductive step to repeatedly
prove the fact for (knock over) the next element.</p>
<p><img src="/veridrone/images/dominoes.jpeg" alt="" /></p>
<p>Now suppose we want to prove some other property <script type="math/tex">Q</script> using induction, but
<script type="math/tex">Q</script> is a property of <em>real</em> numbers. Attempting to apply the same
induction principle, we can prove that <script type="math/tex">Q</script> holds on all non-negative real
numbers if we can prove</p>
<ul>
<li><script type="math/tex">Q</script> holds on 0 and</li>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">Q</script> holds on <script type="math/tex">x</script>, then <script type="math/tex">Q</script> holds on ???</li>
</ul>
<p>It’s not really clear how to complete the inductive step. What is the
“next” real number after <script type="math/tex">x</script>? The real numbers are dense, which says that
for any two real numbers that aren’t the same, there’s another real number
between them. This means that there isn’t really a notion of a next element
in the reals, as there is for natural numbers, lists, trees, and all the
other structures we normally perform induction on. Real numbers are not
like dominoes.</p>
<h3 id="making-it-real">Making it real</h3>
<p>While there isn’t a next real number, we could consider the “next” element
to be a non-empty interval of real numbers. Following this reasoning, the
inductive step for real numbers would require one to prove</p>
<ul>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">Q</script> holds on <script type="math/tex">x</script>, then <script type="math/tex">Q</script> holds on all <script type="math/tex">y \in [x,z)</script> for some <script type="math/tex">z > x</script></li>
</ul>
<p>Unfortunately, this isn’t quite sufficient. The problem is that we can
continue to take positive steps forward without ever covering all the real
numbers. That is, we might only prove <script type="math/tex">Q</script> for all real numbers less than
some bound:</p>
<p><img src="/veridrone/images/Zeno_Dichotomy_Paradox.png" alt="" /></p>
<p>There are a couple ways to resolve this issue. One is to ensure that each
step is always at least as large as some positive constant. However, we’ll
take an alternate approach. In this approach, we’ll make sure that if we
prove <script type="math/tex">Q</script> for all numbers less than some bound (e.g. 1 in the above
image), then we prove it for that bound as well. Formally, this means we
need a second inductive step:</p>
<ul>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">Q</script> holds on all <script type="math/tex">y \in [0,x)</script>, then <script type="math/tex">Q</script> holds on <script type="math/tex">x</script></li>
</ul>
<p>To summarize, the full induction theorem states that we can prove that our
friend <script type="math/tex">Q</script> holds on all non-negative real numbers if we can prove:</p>
<ol type="I">
<li><script type="math/tex">Q</script> holds on 0</li>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">Q</script> holds on all <script type="math/tex">y \in [0,x]</script>, then <script type="math/tex">Q</script> holds on all <script type="math/tex">y \in [x,z)</script> for some <script type="math/tex">z > x</script></li>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">Q</script> holds on all <script type="math/tex">y \in [0,x)</script>, then <script type="math/tex">Q</script> holds on <script type="math/tex">x</script></li>
</ol>
<p>Note that we’ve weakened the first inductive step by assuming that <script type="math/tex">Q</script>
holds on all <script type="math/tex">y \in [0,x]</script>. This is possible for discrete induction as
well.</p>
<p>The proof of real induction proceeds by contradiction. We construct the set
of all elements not satisfying <script type="math/tex">Q</script> and rely on the fact that such a set
has a greatest lower bound. If the greatest lower bound satisfies <script type="math/tex">Q</script>
then this contradicts the first inductive step, and if it does not satisfy
<script type="math/tex">Q</script>, then this contradicts the second inductive step.</p>
<p>The following is a more formal proof, following the proof style from <a href="http://research.microsoft.com/en-us/um/people/lamport/pubs/proof.pdf">this
paper</a>.</p>
<ul class="no-bullet">
<li><textsc>Let</textsc> <script type="math/tex">A=\{x \in \mathbb{R} ~:~ 0 \leq x \wedge \neg Q(x) \}</script>.</li>
<li><textsc>Assume</textsc>: <script type="math/tex">\exists~ x \in A</script></li>
<li><textsc>Prove</textsc>: <script type="math/tex">False</script></li>
<li>⟨1⟩1. <script type="math/tex">\exists~ i : \mathbb{R}</script> such that <script type="math/tex">i</script> is the greatest lower bound of <script type="math/tex">A</script>.
<ul class="no-bullet">
<li><textsc>Proof</textsc>: All non-empty lower bounded sets of reals have a greatest lower bound.</li>
</ul>
</li>
<li>⟨1⟩2. <textsc>Case</textsc>: <script type="math/tex"> i\not\in A</script>.
<ul class="no-bullet">
<li>⟨2⟩1. <script type="math/tex">\forall x \in [0, i],\ Q(x)</script>
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By the definition of greatest lower bound.</li>
</ul>
</li>
<li>⟨2⟩2. <script type="math/tex">\exists~ z > i,\ \forall y \in [i, z),\ Q(y)</script>
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By ⟨2⟩1 and II.</li>
</ul>
</li>
<li>⟨2⟩3. <script type="math/tex">z</script> is a lower bound of <script type="math/tex">A</script>.
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By ⟨2⟩1 and ⟨2⟩2.</li>
</ul>
</li>
<li>⟨2⟩4. QED
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By ⟨2⟩3 and the definition of greatest lower bound.</li>
</ul>
</li>
</ul>
</li>
<li>⟨1⟩3. <textsc>Case</textsc>: <script type="math/tex">i\in A</script>.
<ul class="no-bullet">
<li>⟨2⟩1. <script type="math/tex">\forall ~ 0 \leq x < i,\ Q(x)</script>.
<ul class="no-bullet">
<li><textsc>Proof</textsc>: Since <script type="math/tex">i</script> is a lower bound of <script type="math/tex">A</script></li>
</ul>
</li>
<li>⟨2⟩2. <script type="math/tex">Q(i)</script>
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By III.</li>
</ul>
</li>
<li>⟨2⟩3. QED
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By ⟨2⟩2 and ⟨1⟩3.</li>
</ul>
</li>
</ul>
</li>
<li>⟨1⟩4. QED
<ul class="no-bullet">
<li><textsc>Proof</textsc>: By ⟨1⟩2 and ⟨1⟩3.</li>
</ul>
</li>
</ul>
<p>Of course, I didn’t come up with any of this myself. I took the theorem and
proof from <a href="http://math.uga.edu/~pete/instructors_guide_shorter.pdf">this
paper</a>, which
mentions a number of other papers presenting some version of the theorem.</p>
<p>My esteemed colleague Gregory would also like me to mention that this
result leverages the topology of the real numbers, though I’m not sure if
anyone really knows what that means.</p>
<h3 id="an-application">An Application</h3>
<p>The above paper gives many useful applications of real induction. Here, I’m
going to illustrate how it works on a very simple example, inspired by the
following simple application of discrete induction. Consider an infinite
sequence of numbers <script type="math/tex">x_n</script> where <script type="math/tex">x_0 \geq 0</script> and <script type="math/tex">% <![CDATA[
\forall n,\ x_n <
x_{n+1} %]]></script>. Suppose we want to prove that <script type="math/tex">\forall n, x_n \geq 0</script>. We can
do so by induction over the index into the sequence, which is a natural
number.</p>
<p>Now let’s take a look at a continuous version of this property. Suppose we
have some function <script type="math/tex">f : \mathbb{R} \rightarrow \mathbb{R}</script> such that <script type="math/tex">0
\leq f(0)</script>. Moreover, suppose we know that <script type="math/tex">\forall t \geq 0, f'(t) >
0</script>. We would like to prove that <script type="math/tex">\forall t \geq 0, f(t) \geq 0</script>. As in
the discrete case, we do so using induction, but this time using our
induction principle for the reals. The base case (I) is trivial, so we need
to prove two inductive steps. First,</p>
<ul>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">f(y) \geq 0</script> holds on all <script type="math/tex">y \in [0,x]</script>, then <script type="math/tex">f(y) \geq 0</script> holds on all <script type="math/tex">y \in [x,z)</script> for some <script type="math/tex">z > x</script></li>
</ul>
<p>This follows from the limit definition of a derivative. Informally, there
is some neighborhood around <script type="math/tex">x</script> such that for any point <script type="math/tex">y > x</script> in that
neighborhood, the slope of the line from <script type="math/tex">(x, f(x))</script> to <script type="math/tex">(y, f(y))</script> is
between <script type="math/tex">0</script> and <script type="math/tex">2*f'(x)</script>. In other words, the slope is
nonnegative. More precisely, we instantiate <script type="math/tex">\epsilon</script> in the limit
definition with <script type="math/tex">f'(x)</script> so that for any line, <script type="math/tex">|Slope - f'(x)| \leq
f'(x)</script>. This implies that <script type="math/tex">f(y) \geq f(x)</script>, and <script type="math/tex">f(x)\geq 0</script> by the
induction hypothesis. The size of the neighborhood serves as <script type="math/tex">z-x</script> in the
above property. The following image depicts this:</p>
<p><img src="/veridrone/images/real_ind_post1.svg" alt="" style="width:55%" /></p>
<p>The second inductive step is</p>
<ul>
<li>For any <script type="math/tex">x \geq 0</script>, if <script type="math/tex">f(y) \geq 0</script> holds on all <script type="math/tex">y \in [0,x)</script>, then <script type="math/tex">f(x) \geq 0</script></li>
</ul>
<p>This property follows from continuity of <script type="math/tex">f</script>. Suppose <script type="math/tex">% <![CDATA[
f(x) < 0 %]]></script>. Then
by continuity, there is some other point <script type="math/tex">% <![CDATA[
y < x %]]></script> such that <script type="math/tex">% <![CDATA[
f(y) < 0 %]]></script>,
which is a contradiction.</p>
<p><img src="/veridrone/images/real_ind_post2.svg" alt="" style="width:55%" /></p>
<p>Of course, if we actually wanted to prove this property formally, we
probably wouldn’t rely on such low-level definitions and would probably
instead rely on general theorems about derivatives. Nevertheless, I hope
this example helps to provide some intuition for real induction and a nice
comparison with discrete induction.</p>
Wed, 17 Feb 2016 11:32:17 +0000
http://ucsd-pl.github.io/veridrone/induction/2016/02/17/real-induction.html
http://ucsd-pl.github.io/veridrone/induction/2016/02/17/real-induction.htmlinductionEmbedding Imperative Programs in LTL<p>In this post I’ll describe how we embed certain classes of imperative
programs into LTL, the logic we use to model quadcopter behavior.
Doing so is key to being able to reason about how concrete quadcopter-controller
implementations will behave in the context of the copter’s physical dynamics.</p>
<h3 id="introduction---the-problem">Introduction - The Problem</h3>
<p>Our VeriDrone model of hybrid systems is built on linear temporal logic (LTL).
In LTL, we express valid transitions of the system using relations between a
pre-state (before the transition occurs) and a post-state
(after the transition occurs).
This description of transitions is very expressive; for example, it allows us
to state interesting transitions such as evolution of the system by
differential equations without extending our formalism.
However, this power also introduces a mismatch between the expressiveness of the
model and the actual software that controls the system: there are many logical
expressions in LTL that don’t obviously correspond to valid programs, and
we must also account for the difference between the floating-point computations
done in the controller software and the physical behavior of the drone,
which is modeled in real numbers.
In this post, we will discuss how we address this mismatch by demonstrating a
general technique for embedding programs (expressed using standard
programming-language techniques) in LTL formulas.</p>
<h3 id="our-language">Our Language</h3>
<p>In our case, we’re interested in a simple imperative language. It can
be thought of as a small subset of C, without function-calls, pointers,
or loops: we can manipulate floating-point values, assign them to variables,
and branch on their values. The exclusion of loops and recursion is
crucial: the floating-point analysis techniques we use here
to understand the errors induced by the programs’ floating-point computation
cannot be easily applied to loops (doing so naively would produce
infinitely large error estimates, which is unhelpful.)</p>
<h4 id="syntax">Syntax</h4>
<p>We define the syntax of the programming language as the following type:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Inductive</span><span class="w"> </span><span class="no">cmd</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Seq</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cmd</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cmd</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cmd</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Skip</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cmd</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Asn</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">string</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cmd</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Ite</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cmd</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cmd</span><span class="w"> </span><span class="p">-></span><span class="w"> </span></code></pre></figure>
<p>Here, <code class="highlighter-rouge">Seq</code> is sequencing operator (“semicolon”); <code class="highlighter-rouge">Skip</code> is an
operation with no effect (“no-op”); <code class="highlighter-rouge">Asn</code> assigns a value to a variable
(where the variable is referenced by a <code class="highlighter-rouge">string</code> name).
<code class="highlighter-rouge">Ite</code> implements a particular type of branching (“if-then-else”)
behavior: if the given expression evaluates to a value less than zero, the
first of the two given commands is run; otherwise the second is run.</p>
<p><code class="highlighter-rouge">cexpr</code> is a simple expression language
with floating-point variables and constants, as well as floating-point addition,
subtraction, and multiplication.</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Inductive</span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Var</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">string</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Const</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">float</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Plus</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Minus</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Mult</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cexpr</span><span class="o">.</span></code></pre></figure>
<p>The function <code class="highlighter-rouge">cexprD</code> (the “denotation function”) runs an expression
and returns the result. That is to say, we give a
<em>denotational semantics</em> for our expression language. We omit the definition of
<code class="highlighter-rouge">cexprD</code> for brevity, but here its type:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="no">cexprD</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">cexpr</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">option</span><span class="w"> </span></code></pre></figure>
<p><code class="highlighter-rouge">cexprD</code> returns <code class="highlighter-rouge">None</code> if evaluating the expression “crashes”.
In the language described here, this can only happen in the case
of attempting to look up an undefined variable.</p>
<h4 id="program-state">Program State</h4>
<p>Shortly, we’ll define the semantics of programs (inhabitants of the <code class="highlighter-rouge">cmd</code>
type). To do so, however, we must first formalize a model of program state.
For simplicity, we work in a language where all variables have the
same type. Because we care about modeling the real-time computations
performed by VeriDrone’s controller, the variables are all floating-point
numbers (we build on top of the <a href="http://flocq.gforge.inria.fr/">Flocq</a>
formalization of floating-point numbers); specifically, the model of
IEEE-754 double-precision floating-point contained therein.</p>
<p>Programs have finite state (in the sense of having finitely many variables),
so we use an association list:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Definition</span><span class="w"> </span><span class="no">fstate</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">list</span><span class="w"> </span><span class="o">(</span><span class="no">string</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="no">float</span><span class="o">).</span></code></pre></figure>
<p>This definition is relatively easy to reason about on its own; however, the
representation is not canonical.
This lack of canonicity means that we need to define our own
notion of what it means for <code class="highlighter-rouge">fstate</code>s to be <em>semantically</em> equal.
For example the following two <code class="highlighter-rouge">fstate</code>s are semantically equal,
but not syntactically equal (Coq’s default definition):
<code class="highlighter-rouge">[("a",1.0);("b",2.0)]</code> and <code class="highlighter-rouge">[("b",2.0);("a",1.0)]</code>.</p>
<p>The equivalence we care about is:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Definition</span><span class="w"> </span><span class="no">states_iso</span><span class="w"> </span><span class="o">(</span><span class="no">st</span><span class="w"> </span><span class="no">st'</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">istate</span><span class="o">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Prop</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="kr">forall</span><span class="w"> </span><span class="o">(</span><span class="no">s</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">string</span><span class="o">),</span><span class="w">
</span><span class="kr">match</span><span class="w"> </span><span class="o">(</span><span class="no">fm_lookup</span><span class="w"> </span><span class="no">st</span><span class="w"> </span><span class="no">s</span><span class="o">),</span><span class="w"> </span><span class="o">(</span><span class="no">fm_lookup</span><span class="w"> </span><span class="no">st'</span><span class="w"> </span><span class="no">s</span><span class="o">)</span><span class="w"> </span><span class="kp">with</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">None</span><span class="o">,</span><span class="w"> </span><span class="no">None</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="no">True</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">Some</span><span class="w"> </span><span class="no">f1</span><span class="o">,</span><span class="w"> </span><span class="no">Some</span><span class="w"> </span><span class="no">f2</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="no">pl_equ</span><span class="w"> </span><span class="no">f1</span><span class="w"> </span><span class="no">f2</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="o">_,</span><span class="w"> </span><span class="p">_</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="no">False</span><span class="w">
</span><span class="kr">end</span><span class="o">.</span></code></pre></figure>
<p>That is, two states <code class="highlighter-rouge">st</code> and <code class="highlighter-rouge">st'</code> are equivalent in the necessary
sense if (and only if), for each variable, either</p>
<ul>
<li>Looking up the variable in both states fails, or</li>
<li>Looking up the variable in both states succeeds, and the values read
are equal as PL variables.</li>
</ul>
<p>In order to embed a language, we need to show (among other things)
that this notion of equality is respected by that particular
programming-language semantics.</p>
<p>The type of states used in the implementation of LTL that VeriDrone runs on
is somewhat different from the imperative language’s <code class="highlighter-rouge">fstate</code>. Here it is:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Definition</span><span class="w"> </span><span class="no">state</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="o">(</span><span class="no">string</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">R</span><span class="o">).</span></code></pre></figure>
<p>This is not a finite map, since all states must assign values to all
variables. In practice, we only care about a subset of the variables;
the rest are essentially populated with default, dummy values. A result
of this type mismatch is that many functions and theorems need to
carry around a <code class="highlighter-rouge">list</code> of <code class="highlighter-rouge">string</code>s describing what variables
are considered relevant to the program at hand.</p>
<h4 id="semantics">Semantics</h4>
<p>To embed programs in LTL, we need a semantics for the language
we’re embedding. In this case we follow the standard approach of giving
the language a <em>big-step operational semantics</em>
in terms of an inductively-defined relation that describes the transitions
of entire programs in terms of their starting and ending states.
(Note that, because the semantics is total and deterministic.
we could have just given it as a function. However, leaving it as a relation
makes it easier to extend the system to nondeterministic or nontotal programs,
though doing so in our case is future work.)</p>
<p>We encode the semantics of our language as an inductive relation over
<code class="highlighter-rouge">(fstate, cmd, fstate)</code> triples.
<code class="highlighter-rouge">evals st p st'</code> states that when the program <code class="highlighter-rouge">p</code> is run
from the state <code class="highlighter-rouge">st</code> it will terminate in the state <code class="highlighter-rouge">st'</code>.</p>
<p>Formally, here is the semantics for our command language. This is a relatively
standard semantics, which I’ve already described at a high level above, so I
won’t go into further detail here. One noteworthy thing about this semantics
is that it can “crash” if the evaluation of an expression fails
(<code class="highlighter-rouge">cexprD</code> returns <code class="highlighter-rouge">None</code>). Crashing is represented by a
lack of transition in the <code class="highlighter-rouge">eval</code> relation.</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Inductive</span><span class="w"> </span><span class="no">eval</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">fstate</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">cmd</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">fstate</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="kr">Prop</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">ESkip</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">s</span><span class="o">,</span><span class="w"> </span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">Skip</span><span class="w"> </span><span class="no">s</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">ESeq</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="no">s''</span><span class="w"> </span><span class="no">a</span><span class="w"> </span><span class="no">b</span><span class="o">,</span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">a</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="no">b</span><span class="w"> </span><span class="no">s''</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">(</span><span class="no">Seq</span><span class="w"> </span><span class="no">a</span><span class="w"> </span><span class="no">b</span><span class="o">)</span><span class="w"> </span><span class="no">s''</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">EAsn</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">forall</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">e</span><span class="w"> </span><span class="no">val</span><span class="o">,</span><span class="w">
</span><span class="no">cexprD</span><span class="w"> </span><span class="no">e</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Some</span><span class="w"> </span><span class="no">val</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">(</span><span class="no">Asn</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">e</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">update</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">val</span><span class="o">)</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">EIteTrue</span><span class="w"> </span><span class="p">:</span><span class="w">
</span><span class="kr">forall</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="no">ex</span><span class="w"> </span><span class="no">c1</span><span class="w"> </span><span class="no">c2</span><span class="o">,</span><span class="w">
</span><span class="no">cexprD</span><span class="w"> </span><span class="no">ex</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Some</span><span class="w"> </span><span class="no">f</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">f</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="no">fzero</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">c1</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">(</span><span class="no">Ite</span><span class="w"> </span><span class="no">ex</span><span class="w"> </span><span class="no">c1</span><span class="w"> </span><span class="no">c2</span><span class="o">)</span><span class="w"> </span><span class="no">s'</span><span class="w">
</span><span class="o">|</span><span class="w"> </span><span class="no">EIteFalse</span><span class="p">:</span><span class="w">
</span><span class="kr">forall</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="no">ex</span><span class="w"> </span><span class="no">c1</span><span class="w"> </span><span class="no">c2</span><span class="w"> </span><span class="no">r</span><span class="o">,</span><span class="w">
</span><span class="no">cexprD</span><span class="w"> </span><span class="no">ex</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Some</span><span class="w"> </span><span class="no">f</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">f</span><span class="w"> </span><span class="o">>=</span><span class="w"> </span><span class="no">fzero</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">c2</span><span class="w"> </span><span class="no">s'</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">(</span><span class="no">Ite</span><span class="w"> </span><span class="no">ex</span><span class="w"> </span><span class="no">c1</span><span class="w"> </span><span class="no">c2</span><span class="o">)</span><span class="w"> </span></code></pre></figure>
<p>(<code class="highlighter-rouge">fzero</code> is shorthand for the floating-point constant 0, whose full name
is annoying to type out in Coq. <code class="highlighter-rouge"><</code> and <code class="highlighter-rouge">gt</code> are used to abbreviate
floating-point comparison functions.)</p>
<h3 id="embedding">Embedding</h3>
<p>Embedding our simple language inside of LTL requires us to turn a
program - expressed as a syntactic object, with semantics provided by
an inductive relation, as above - into a formula describing the
<em>entire execution of the program</em> as a single state-transition; that is,
essentially, a predicate over <code class="highlighter-rouge">(s1 : state, s2 : state)</code> pairs, which is
satisfied for a given program <code class="highlighter-rouge">p</code> if and only if <code class="highlighter-rouge">eval s1 p s2</code>
is satisfied.</p>
<p>We use the following module to build embeddings:</p>
<p>Embeddings will have (essentially) the following data type:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="no">embedding</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Type</span><span class="w"> </span><span class="p">:=</span><span class="w"> </span><span class="no">list</span><span class="w"> </span><span class="no">string</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">ast</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="nn">LTL</span><span class="p">.</span></code></pre></figure>
<p>The first argument is a list of variables to consider when embedding the
program, needed for the technical reason mentioned above. The second
argument is the program itself.
I call the type of programs <code class="highlighter-rouge">ast</code> rather than <code class="highlighter-rouge">cmd</code> here,
to emphasize the fact that we seek to be parametric in the
particular syntax and semantics that the program is expressed in
(recall that the goal is a general solution, not a solution tailored to a
particular language or semantics). In practice this means that we can’t
ever do an explicit pattern-match on the constructors of <code class="highlighter-rouge">ast</code>.</p>
<p>However, there are some wrinkles: first, as mentioned, LTL expresses
its states as functions (<code class="highlighter-rouge">string -> R</code>) giving a value to every variable;
our imperative language uses a finite map that only gives values to variables
used in the program; we deal with this by passing around variable-lists
as described above.</p>
<p>Second, and more importantly, our implementation of LTL is a logic over
real numbers, in order to make it easier to reason about the physical
behaviors of our system. Thus we’ll need to make a formal connection
between the floating-point computations of our langauge and the
real-valued computations expressed in the logic. The gory details of
this connection are beyond the scope of this post, but I’ll
touch on this topic again a bit later on.</p>
<h4 id="correctness-of-embedding">Correctness of Embedding</h4>
<p>What does it mean for our definition of embedding to be correct?
Obviously there are many functions that satisfy the <code class="highlighter-rouge">embedding</code>
type above. In order to express what we mean by a <em>correct</em> embedding
function, we first need to define a predicate relating floating-point
program states to real-valued LTL states. Doing so will allow us to
make a formal connection between evaluations of concrete programs
and the evaluation of LTL logical formulas.</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Definition</span><span class="w"> </span><span class="no">models</span><span class="w"> </span><span class="o">(</span><span class="no">vars</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">list</span><span class="w"> </span><span class="no">string</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">ist</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">istate</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">sst</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">lstate</span><span class="o">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Prop</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="kr">forall</span><span class="w"> </span><span class="o">(</span><span class="no">s</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">string</span><span class="o">),</span><span class="w">
</span><span class="o">(</span><span class="no">In</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">vars</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="kp">exists</span><span class="w"> </span><span class="o">(</span><span class="no">d</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">pl_data</span><span class="o">),</span><span class="w">
</span><span class="no">fm_lookup</span><span class="w"> </span><span class="no">ist</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">Some</span><span class="w"> </span><span class="no">d</span><span class="w"> </span><span class="o">/\</span><span class="w">
</span><span class="no">asReal</span><span class="w"> </span><span class="no">d</span><span class="w"> </span><span class="o">(</span><span class="no">sst</span><span class="w"> </span><span class="no">s</span><span class="o">))</span><span class="w"> </span><span class="o">/\</span><span class="w">
</span><span class="o">(~</span><span class="no">In</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="no">vars</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="no">fm_lookup</span><span class="w"> </span><span class="no">ist</span><span class="w"> </span><span class="no">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">None</span><span class="o">).</span></code></pre></figure>
<p>In our case, <code class="highlighter-rouge">pl_data</code> is an alias for the type of floating-point numbers
(<code class="highlighter-rouge">float</code>); <code class="highlighter-rouge">asReal f r</code> expresses the fact that, when the
value <code class="highlighter-rouge">f</code> is converted to a real number, the conversion succeeds
(in our case, this means that the floating-point value
retrieved was not Inf or NaN, so-called “exceptional values”
in floating-point), and yields the number <code class="highlighter-rouge">r</code>.</p>
<p><code class="highlighter-rouge">istate</code> - “implementation state” - and <code class="highlighter-rouge">ls</code> - “logical state”,
are respectively <code class="highlighter-rouge">fstate</code> and <code class="highlighter-rouge">state</code> in our case.
<code class="highlighter-rouge">fm_lookup</code> simply looks up a variable in an
<code class="highlighter-rouge">istate</code>, returning <code class="highlighter-rouge">None</code> if the lookup fails.</p>
<p>Informally, this definition of <code class="highlighter-rouge">models</code> imposes
the following requirement on all variables:</p>
<ol>
<li>For each variable <code class="highlighter-rouge">v</code> that we care about
(i.e., variables in the list <code class="highlighter-rouge">vars</code>) the variable should
be present in <code class="highlighter-rouge">ist</code>, and the result of looking up that
variable should be a real number <code class="highlighter-rouge">d</code> that equals the result
of looking up the same variable <code class="highlighter-rouge">v</code> in the real-valued state.
That is, the floating-point and real-valued states
should “agree” on these variables.</li>
<li>For all other variables, we require that they not be present
in the state <code class="highlighter-rouge">ist</code> (looking them up in the state should fail).</li>
</ol>
<p>Armed with this definition, we can express the correctness property that
we want to hold over our candidate function for embedding programs into
LTL. What we’d like to have is, essentially, the following
diagram “commute”, in that the result of taking the two paths
shown (labeled “Path 1” and “Path 2”) should be the same.</p>
<div><img src="/veridrone/images/EmbeddingCorrectness.svg" alt="Correctness Diagram" style="height: 450px;" /></div>
<p>Informally, suppose we have a program <code class="highlighter-rouge">p</code> and we know that
<code class="highlighter-rouge">eval fs1 p fs2</code>. Then the embedding of <code class="highlighter-rouge">p</code>
(informally, <code class="highlighter-rouge">embed p</code>, omitting the variable-list argument
for brevity) should be able to take a step from <code class="highlighter-rouge">rs1</code> to
<code class="highlighter-rouge">rs2</code> (according to LTL evaluation semantics, written as
<code class="highlighter-rouge">eval_formula</code> in the diagram), where <code class="highlighter-rouge">rs1</code> is a real-valued
LTL state modeling the floating-point state <code class="highlighter-rouge">fs1</code>
according to the <code class="highlighter-rouge">models</code> relation; and likewise for
<code class="highlighter-rouge">rs2</code> and <code class="highlighter-rouge">fs2</code>.</p>
<p>Writing this out formally gives us:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="k">Definition</span><span class="w"> </span><span class="no">embedding_correct1</span><span class="w"> </span><span class="o">(</span><span class="no">embed</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">embedding</span><span class="o">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Prop</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="kr">forall</span><span class="w"> </span><span class="o">(</span><span class="no">v</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">list</span><span class="w"> </span><span class="no">string</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">prg</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">ast</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">is</span><span class="w"> </span><span class="no">is'</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">istate</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">ls</span><span class="w"> </span><span class="no">ls'</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nn">LTL</span><span class="p">.</span><span class="no">state</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">tr</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">stream</span><span class="w"> </span><span class="nn">LTL</span><span class="p">.</span><span class="no">state</span><span class="o">),</span><span class="w">
</span><span class="no">models</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">is</span><span class="w"> </span><span class="no">ls</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">models</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">is'</span><span class="w"> </span><span class="no">ls'</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">is</span><span class="w"> </span><span class="no">prg</span><span class="w"> </span><span class="no">is'</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="nn">LTL</span><span class="p">.</span><span class="no">eval_formula</span><span class="w">
</span><span class="o">(</span><span class="no">embed</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">prg</span><span class="o">)</span><span class="w">
</span><span class="o">(</span><span class="no">ls</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="no">ls'</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="no">tr</span><span class="o">)</span></code></pre></figure>
<p>It’s worth noting here that some of the primitives (<code class="highlighter-rouge">LTL.*</code> in particular)
have slightly different names in our Coq development; I’ve renamed them
here for clarity. <code class="highlighter-rouge">stream</code> is an infinite (co-inductive) list;
<code class="highlighter-rouge">LTL.eval_formula</code> uses this type in order to be able to express
temporal properties about potentially infinite runs of systems.</p>
<p>We also want to make sure that crashing behavior is preserved:
if the source program crashed (i.e., can’t take a step),
its corresponding LTL formula should not be able to take a step.
(We say that the LTL formula is not “enabled”, to borrow a term
often used in the TLA variant of LTL).</p>
<p>Written out formally, we have:</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">embedding_correct2</span><span class="w"> </span><span class="o">(</span><span class="no">embed</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">embedding</span><span class="o">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kr">Prop</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="kr">forall</span><span class="w"> </span><span class="o">(</span><span class="no">v</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">list</span><span class="w"> </span><span class="no">string</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">prg</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">ast</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">is</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">istate</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">ls</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nn">LTL</span><span class="p">.</span><span class="no">state</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">tr</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">stream</span><span class="w"> </span><span class="nn">LTL</span><span class="p">.</span><span class="no">state</span><span class="o">),</span><span class="w">
</span><span class="no">models</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">is</span><span class="w"> </span><span class="no">ls</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="o">~(</span><span class="kp">exists</span><span class="w"> </span><span class="no">is'</span><span class="o">,</span><span class="w"> </span><span class="no">eval</span><span class="w"> </span><span class="no">is</span><span class="w"> </span><span class="no">prg</span><span class="w"> </span><span class="no">is'</span><span class="o">)</span><span class="w"> </span><span class="p">-></span><span class="w">
</span><span class="o">~(</span><span class="nn">LTL</span><span class="p">.</span><span class="no">eval_formula</span><span class="w">
</span><span class="o">(</span><span class="no">Enabled</span><span class="w"> </span><span class="o">(</span><span class="no">embed</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">prg</span><span class="o">))</span><span class="w">
</span><span class="o">(</span><span class="no">ls</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="no">tr</span><span class="o">)).</span></code></pre></figure>
<p>A correct embedding function must meet <em>both</em> of these conditions.</p>
<h4 id="our-embedding-function">Our Embedding Function</h4>
<p>We use the following embedding function, which we’ve proven correct
(in the sense of proving that it meets both of the correctness
conditions spelled out above). For details of the proof, the interested
reader can consult the following
<a href="https://gist.github.com/mmalvarez/d3c87466a3cff104dddb#file-embed-v-L225">excerpt from our Coq development</a>
(the full development is not yet publicly available).</p>
<figure class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="w"> </span><span class="k">Definition</span><span class="w"> </span><span class="no">embed_ex</span><span class="w"> </span><span class="o">(</span><span class="no">v</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">list</span><span class="w"> </span><span class="no">string</span><span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="no">prg</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">ast</span><span class="o">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="no">Formula</span><span class="w"> </span><span class="p">:=</span><span class="w">
</span><span class="nn">LTL</span><span class="p">.</span><span class="no">Embed</span><span class="w"> </span><span class="o">(</span><span class="kr">fun</span><span class="w"> </span><span class="no">lpre</span><span class="w"> </span><span class="no">lpost</span><span class="w"> </span><span class="p">=></span><span class="w">
</span><span class="kp">exists</span><span class="w"> </span><span class="no">cpre</span><span class="w"> </span><span class="no">cpost</span><span class="o">,</span><span class="w">
</span><span class="no">models</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">cpre</span><span class="w"> </span><span class="no">lpre</span><span class="w"> </span><span class="o">/\</span><span class="w">
</span><span class="no">models</span><span class="w"> </span><span class="no">v</span><span class="w"> </span><span class="no">cpost</span><span class="w"> </span><span class="no">lpost</span><span class="w"> </span><span class="o">/\</span><span class="w">
</span><span class="no">eval</span><span class="w"> </span><span class="no">cpre</span><span class="w"> </span><span class="no">prg</span><span class="w"> </span><span class="no">cpost</span><span class="o">).</span></code></pre></figure>
<p>Here we use the <code class="highlighter-rouge">LTL.Embed</code> construct to
embed a step-predicate expressed in Coq’s logic (that is, of type
<code class="highlighter-rouge">TLA.state -> TLA.state -> Prop</code>). In this case, we embed a
formula that requires the existence of appropriate “concrete states”
or “implementation states” (which have type <code class="highlighter-rouge">istate</code> in general;
or <code class="highlighter-rouge">fstate</code> in our case), such that the first one models the
initial TLA state, the second models the second, and the program
steps from the initial to the final concrete state according to the
evaluation semantics for the language.</p>
<p>However, it is worth noting that this definition of embedding is only correct
if the evaluation relation giving semantics to the embedded programming
language is deterministic (i.e., the language is a deterministic language).
The problem becomes more complicated in the face of nondeterminism:
consider, for instance, a program that nondeterministically fails.
It is less obvious how to construct an embedding function that will
preserve this behavior in LTL. The implementation given above would simply
throw away failures/”crashes” and
only preserve successful runs, but in practice we care greatly about
cases where the system crashes (because we must prevent them).
Since we do not need to support writing quadcopter controllers in
a nondeterministic source language, the definition given here suffices.</p>
<h3 id="floating-point-reasoning">Floating-Point Reasoning</h3>
<p>Of course, there’s still one missing piece here: making a formal connection
between the real-valued arithmetic used in our LTL model and the
floating-point arithmetic that controller programs (<code class="highlighter-rouge">cmd</code>s) run on.
We use a sound, interval-based overapproximation to safely capture
floating-point error. However, that’s a story for another day.
The interested reader can consult the
<a href="https://gist.github.com/mmalvarez/9f6efaae22aa07674251">implementation</a>.</p>
<h3 id="source">Source</h3>
<p>The full source code for this project isn’t yet available, but I’ve created
two Gists that contain files relevant to this post.</p>
<ul>
<li><a href="https://gist.github.com/mmalvarez/d3c87466a3cff104dddb">Embed.v</a>
has general definitions related to embedding.</li>
<li><a href="https://gist.github.com/mmalvarez/9f6efaae22aa07674251">FloatEmbed.v</a>
implements the Embed abstraction for our floating-point language</li>
</ul>
<h3 id="acknowledgements">Acknowledgements</h3>
<p>I’d like to acknowledge Gregory Malecha, with whom I worked on this project,
and who helped to refine this blog post, and Sorin Lerner, my advisor.</p>
Sat, 28 Nov 2015 00:00:00 +0000
http://ucsd-pl.github.io/veridrone/quadcopter/semantics/floating-point/2015/11/28/embedding-in-tla.html
http://ucsd-pl.github.io/veridrone/quadcopter/semantics/floating-point/2015/11/28/embedding-in-tla.htmlquadcoptersemanticsfloating-pointShort introduction to the VeriDrone project<p>Here is a brief introduction to the motivation, goals, and current progress
of the VeriDrone project. This was a short presentation given to industry
experts at a recent research review.</p>
<center>
<video width="75%" controls="">
<source src="/veridrone/videos/ShortIntro-CNS-review.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
</center>
Thu, 22 Oct 2015 19:08:10 +0000
http://ucsd-pl.github.io/veridrone/talk/2015/10/22/intro-cns-review.html
http://ucsd-pl.github.io/veridrone/talk/2015/10/22/intro-cns-review.htmltalkModel meets reality: practical monitor verification<p>Back in July, we built a software module to “guarantee” that a quadcopter
never leaves a rectangular box. We spent months <em>proving</em> this guarantee as
a mathematical theorem. We were pretty proud of ourselves, until we
actually bothered to run our code. As it turns out, when it comes to
cyber-physical systems, there’s more to it then just proving theorems.</p>
<h3 id="the-problem">The problem</h3>
<p>The purpose of the software module, which we call a monitor, is to allow
the pilot or existing control software to move the quadcopter freely within
a rectangular region but prevent it from ever leaving that rectangular
region.</p>
<table>
<thead>
<tr>
<th style="text-align: center">Quadcopter inside boundary</th>
<th style="text-align: center">Quadcopter approaching boundary</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><img src="/veridrone/images/pilot-within-box.jpg" alt="" style="width:85%" /></td>
<td style="text-align: center"><img src="/veridrone/images/pilot-at-boundary.jpg" alt="" style="width:85%" /></td>
</tr>
</tbody>
</table>
<p>We proved that our monitor guarantees that the quadcopter will never leave
the safe rectangular box, <em>assuming a simple model of the quadcopter
dynamics</em>. This is a key point. In particular, our model doesn’t include a
number of forces like wind. So as it turns out, when you actually fly a
quadcopter with our monitor, it does occasionally leave the rectangular
safe region. Minor violations are ok; in fact, they are inevitable in this
domain since a model of the physical dynamics can never include all the
details. But what happens when the quadcopter leaves the rectangular safe
region? As the following figure depicts, the quadcopter gets stuck:</p>
<p><img src="/veridrone/images/pilot-outside-box.jpg" alt="" style="width:45%" /></p>
<p>A <a href="/veridrone/quadcopter/test/2015/08/07/quadcopter-stuck.html">prior post</a>
discusses why the quadcopter gets stuck. To summarize, while running
our rectangular bounding monitor, when the quadcopter was pushed outside
any of the boundaries by wind or other unmodeled forces, the logic of the
monitor prevented the quadcopter from moving back inside the boundaries,
regardless of what the pilot did. In this post, I’m going to describe
potential solutions to this issue.</p>
<h3 id="potential-solutions">Potential solutions</h3>
<p>This issue is not a violation of any property we verified. Within our model
of the dynamics of the quadcopter, the monitor prevents the quadcopter from
ever leaving the rectangular boundaries, so there is no need to prove
anything about what happens when the quadcopter is outside. However, when
working with cyber-physical systems, there is always a gap between model
and reality. In order to build a practical verified monitor, we need to
verify something about what happens when the quadcopter is outside the
boundaries enforced by the monitor. What exactly should we verify?</p>
<p>Informally, we want the quadcopter to behave reasonably with respect the
the safe region defined by the monitor’s boundaries. For example, we want
the quadcopter to stay close to the safe region if it starts close and to
eventually move back inside the safe region if it starts outside. Moreover,
we want whatever property we verify to reduce to our original safety
property when the quadcopter starts within the safe region (i.e. the
quadcopter stays within the safe region if it starts within the safe
region<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup>).</p>
<p><img src="/veridrone/images/move-back-inside.jpg" alt="" style="width:45%" /></p>
<p>These informal notions of reasonable behavior are very similar to several
core notions in control theory called Lyapunov stability. Roughly speaking,
Lyapunov stability characterizes the behavor of a system of differential
equations around an equilibrium (a point <script type="math/tex">x_e</script> such that if the system is
initially at <script type="math/tex">x_e</script>, then it remains at <script type="math/tex">x_e</script> for all time). There are
three common notions of stability in the theory of Lyapunov:</p>
<ol>
<li>A system is <strong>Lyapunov stable</strong> if it can remain within any distance
<script type="math/tex">\epsilon</script> of the equilibrium as long as it starts within some distance
<script type="math/tex">\delta</script> of the origin. In other words, a system will remain arbitrarily
close to the equilbirium as long as it starts sufficiently close.</li>
<li>A system is <strong>asymptotically stable</strong> if it is Lyapunov stable and
approaches the equilibrium as time goes to infinity. In other words, the
system remains close to the equilibrium and eventually converges to it.</li>
<li>A system is <strong>exponentially stable</strong> if it is asymptotically stable and if
the distance to the equilibrium over time is bounded by an exponential
function. In other words, the system remains close to the equilbirium and
converges exponentially quickly to it.</li>
</ol>
<p>These three definitions of stability seem to capture the kind of reasonable
behavior I described earlier, except that our system (a quadcopter equiped
with our rectangular bounding monitor) does not have an equibrium point. In
other words, there is no single point such that if the quadcopter reaches
that point, it should remain there for all time. However, we do have a
<em>region</em> such that if the system reaches that region, it should remain
there for all time. This region is the safe region. We can
easily adapt the three notions of stability to our setting by replacing
“distance to the equilibrium” with “distance to the safe
region”.</p>
<p>Now we have three definitions of stability that apply to our setting. Which
one do we actually use? The first definition, Lyapunov stable, does not
require that the system ever re-enter the safe region if it leaves the safe
region, so it permits the system to remain stuck on the boundary. The
second definition, asymptotic stability, does require the system to
converge to the safe region, but there is no bound on how long this must
take. For all practical purposes, this could still allow the system to
remain stuck on the boundary. Thus, it seems that the final definition,
exponential stability, is the appropriate one. It requires not only that
the system converge to the safe region but that it do so quickly.</p>
<p>We have not yet verified or tested the appropriateness of stability for
capturing reasonable behavior of the quadcopter outside of the safe
region. In fact, stability is not the only potential solution to our
problem. Here are a couple other potential solutions that we are
considering:</p>
<ol>
<li>
<p>We could treat the problem as an optimization problem in which the
monitor seeks to minimize some notion of “badness”. One possible definition
of badness is the distance from the safe region. There is a tremendous
amount of work on optimial control theory that we could draw from.</p>
</li>
<li>
<p>The quadcopter only leaves the safe region if there are unmodeled forces
such as wind. What happens when there is a constant unmodeled force like a
constant wind? Even an exponentially stable system may not converge to the
safe region in the presence of a constant wind. Thus, we may want to verify
that the system quickly converges to the safe region in the presence of
such an unmodeled force. Again, there are ideas from control theory that we
can draw on, such as the use of the I term in a PID controller.</p>
</li>
</ol>
<p>We haven’t yet experimentally evaluated any of these ideas, but as always,
this is very important. As soon as we do so, we will certainly write
another blog post on the results.</p>
<hr />
<hr />
<div class="footnotes">
<ol>
<li id="fn:1">
<p><small>We actually need to replace safe region with inductively safe region. The inductively safe region is a subset of the safe region such that if the system starts in any part of the inductively safe region, it remains there. To see that this is not the entire safe region, suppose the quadcopter starts just inside the upper boundary traveling at near infinite upward velocity. Even though the system is within the safe region, it will soon exit the safe region. Thus, it is not in the inductively safe region.</small> <a href="#fnref:1" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>
Tue, 20 Oct 2015 11:12:27 +0000
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/10/20/quadcopter-stuck-solution.html
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/10/20/quadcopter-stuck-solution.htmlquadcoptertestTalk at MEMOCODE 2015<p>We just got back from <a href="http://memocode.irisa.fr/2015/index.html">MEMOCODE
2015</a>. The conference was a bit
outside our normal community, which made it a great experience. We had an
opportunity to speak with and hear talks from some cyber-physical systems
experts like <a href="http://www.public.asu.edu/~gfaineko/">Georgios Fainekos</a>,
<a href="http://es.cs.uni-kl.de/people/schneider/">Klaus Schneider</a>, and <a href="http://software.imdea.org/people/pavithra.prabhakar/">Pavithra
Prabhakar</a> and to see
an interesting keynote presentation from <a href="https://www.cis.upenn.edu/~alur/">Rajeev
Alur</a>.</p>
<p>Our own talk was well received. We received some interesting questions on
our proof calculus and on the relationship between our model and the
electrical signals that actually get sent to the motors. The later question
is an interesting topic for a future blog post.</p>
<p>Below is a recording of our talk. Let us know what you think.</p>
<center>
<video width="75%" controls="">
<source src="/veridrone/videos/MEMOCODE-15-talk.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
</center>
Fri, 25 Sep 2015 21:07:00 +0000
http://ucsd-pl.github.io/veridrone/talk/2015/09/25/memocode-15-talk.html
http://ucsd-pl.github.io/veridrone/talk/2015/09/25/memocode-15-talk.htmltalkAggressive Flying to Test Height Monitor<div style="float:left;margin:0 15px 5px 0; width:50%">
<video width="100%" controls="">
<source src="/veridrone/videos/agressive-flying-height-limiter.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
<p>Here is a video where we are testing our Coq verified altitude limiter
with some aggressive flying close to the height boundary. First, I try
flying up to the altitude boundary while flying aggressively to the
right. Then, with throttle pushed all the way up, I try various
motions in other directions, including so-called yaw (essentially
pivoting). As you can see, the altitude limiter keeps engaging (since
the throttle is all the way up), and it keeps the quadcopter within
the appropriate bounds, while allowing control in other dimensions.</p>
<div style="clear:both;"></div>
Thu, 17 Sep 2015 11:00:00 +0000
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/09/17/aggressive-flying.html
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/09/17/aggressive-flying.htmlquadcoptertestFun anecdote about the controller<p>We’re catching up with our video archive, and I found this fun video
that illustrates the kinds of non-research fun we’ve been having! A
frustrating problem that took us a long time to fix, and caused us to
cry out “D’oh”!</p>
<p>So we were about to try a new test flight, and the quadcopter was
resting on the athletic fields when we usually run our test flights. I
started increasing upward thrust to get off the ground, and usually
the quadcopter would smoothly start lifting upward. But this time, the
quadcopter instead started tilting towards the right. Only two legs
would get off the ground, with the other two still on the ground. It
felt as though the quadcopter would just flip over before even taking
off! I pushed the throttle down, and tried gradually upward again, but
the same thing happened. And it happened over and over, and each time
the quacopter would rise on two legs I would bring it back down, for
fear of flipping. This felt extremely unusual. Our monitors weren’t
even enabled yet! This video shows what was happening, and you can
hear me complaining how something is wrong:</p>
<center>
<video width="75%" controls="">
<source src="/veridrone/videos/controller-problems.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
</center>
<p>“It’s just wind!” said Dan. “Just gun it upwards and it will get past
the turbulence!” And indeed it was windy… and indeed, turbulence
can be worse at ground level because the rotors are blowing wind into
the ground… but still… we had lots wind before and nothing caused
this kind of tilting.</p>
<p>I felt something was wrong, something was off. So we tried the
time-tested remedy to many of our problems: reboot the quadcopter! So
disconnect battery, and put it back in. This often fixes odd
intermittent problems, since it resets the GPS, and all sensors, but
this time, it didn’t. Same problem! We then re-calibrated the
compass, and made sure the GPS was getting enough
satellites. But the problem persisted. We reverted to the stable
version of the software that didn’t have our changes on it, and still
the same thing.</p>
<p>Dan said: “Look we tried everything, it all seems ok, and it was
working before. It’s probably wind, just gun it upwards!” Dan is
cavalier, but me, not so much. I was reluctant. But by this point,
we’re all reaching our limit, and we had to try something, so I was
tempted to “gun it upwards”.</p>
<p>But then I noticed something. The display on the controller looked a
little different than usual. And then it all came together: the
fine-tuning knobs! The left and right stick on most controllers have
some sort of fine-tuning, which essentially allows you to set what
value zero position of the stick means. This can be useful in some
manual modes to fine tune things so that when your pitch/roll stick is
at the zero position (where zero position means you’re just not
touching it and it stays at its center), the quadcopter stays put (and
if it doesn’t, there are small knobs/buttons that you can use on the
controller to set the fine-tuning). It turns out in this case, someone
accidentally pushed the fine-tuning knobs all the way to the right, so
that the zero position really meant “go all the way to the
right”. Once we fixed the fine-tuning back to normal, everything
worked as before. “D’oh”!!!</p>
<p><strong>Lesson 1:</strong> always check your fine-tuning!</p>
<p><strong>Lesson 2:</strong> If something feels
odd and wrong, be careful. We could easily have broken some propellers
if we gunned it all the way up (and I really was about to do it!!!)</p>
Tue, 08 Sep 2015 11:12:17 +0000
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/09/08/controller-problems.html
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/09/08/controller-problems.htmlquadcoptertestQuadcopter stuck outside boundaries<p>In an <a href="/veridrone/quadcopter/test/2015/07/17/fall-at-bounds.html">earlier post</a>, I mentioned
that the first test of our rectangular bounding monitor had a few
interesting problems, and I described one of them. In this post, I’ll
describe another interesting problem that occurred during the test, and the
cause that we later discovered.</p>
<h3 id="the-problem">The problem</h3>
<p>The purpose of the monitor is to allow the pilot or existing control
software to move the quadcopter freely within a rectangular region but
prevent it from ever leaving that rectangular region.</p>
<table>
<thead>
<tr>
<th style="text-align: center">Quadcopter inside boundary</th>
<th style="text-align: center">Quadcopter approaching boundary</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><img src="/veridrone/images/pilot-within-box.jpg" alt="" style="width:85%" /></td>
<td style="text-align: center"><img src="/veridrone/images/pilot-at-boundary.jpg" alt="" style="width:85%" /></td>
</tr>
</tbody>
</table>
<p>When the quadcopter was on the border of the bounding box, it sometimes
appeared to get stuck. That is, when Sorin attempted to move the quadcopter
towards the middle of the bounding box, the monitor ignored his comments,
even though they seemed to be perfectly safe.</p>
<p><img src="/veridrone/images/pilot-stuck.jpg" alt="" style="width:45%" /></p>
<p>This didn’t always occur when the quadcopter was on the boundary; it only
occurred sometimes.</p>
<h3 id="the-cause">The cause</h3>
<p>In the <a href="/veridrone/quadcopter/test/2015/07/17/fall-at-bounds.html">earlier post</a>, I gave the
following pseudo-code for the monitor:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">AX</span> <span class="o">=</span> <span class="n">A</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">Theta</span><span class="p">)</span>
<span class="n">AZ</span> <span class="o">=</span> <span class="n">A</span><span class="o">*</span><span class="n">cos</span><span class="p">(</span><span class="n">Theta</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">Safe</span><span class="p">(</span><span class="n">AX</span><span class="p">,...)</span> <span class="o">&&</span> <span class="n">Safe</span><span class="p">(</span><span class="n">AZ</span><span class="p">,...))</span> <span class="p">{</span>
<span class="n">issue</span> <span class="n">A</span> <span class="n">and</span> <span class="n">Theta</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">Safe</span><span class="p">(</span><span class="n">AZ</span><span class="p">,...)</span> <span class="o">&&</span> <span class="p">...)</span> <span class="p">{</span>
<span class="n">issue</span> <span class="n">polar</span><span class="p">(</span><span class="n">AZ</span><span class="p">,</span> <span class="n">defaultX</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">Safe</span><span class="p">(</span><span class="n">AX</span><span class="p">,...)</span> <span class="o">&&</span> <span class="p">...)</span> <span class="p">{</span>
<span class="n">issue</span> <span class="n">polar</span><span class="p">(</span><span class="n">defaultZ</span><span class="p">,</span> <span class="n">AX</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">issue</span> <span class="n">polar</span><span class="p">(</span><span class="n">defaultZ</span><span class="p">,</span> <span class="n">defaultX</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>
<p>The monitor first converts the pilot’s desired roll angle (<code class="highlighter-rouge">Theta</code>) and
acceleration (<code class="highlighter-rouge">A</code>) to rectangular coordinates, and then checks them for
safety, taking different actions depending on which safety checks pass (see
the earlier post for more details).</p>
<p>At first glance, it doesn’t seem like this would cause the quadcopter to
get stuck at the boundaries. As long as the quadcopter remains within
bounds, the monitor should always allow the pilot to steer the vehicle back
towards the center of the bounding box.</p>
<p>Unfortunately, the quadcopter does not always stay within bounds; it
occasionally leaves the box by a small amount, generally less than 1
meter. While this might seem to indicate that our formal verification
result is useless, this sort of violation is actually inevitable in
cyber-physical systems. The theorem that we prove in Coq is only with
respect to a <em>model</em> of the physical world, and no model can account for
everything (e.g. wind). To take an extreme example, if a tornado flies into
the bounding box, the quadcopter might not stay inside.</p>
<p>Thus, no matter how detailed our model is, we should account for the
possibility that the quadcopter leaves the bounding box. Let’s go back to
the code to take a look at what happens when the quadcopter is outside one
of the boundaries. As it turns out, the safety checks will reject any
desired acceleration from the pilot, even if it will bring the quadcopter
back inside the boundary. Thus, the monitor will take the last branch,
issuing the default accelerations for both dimensions:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">issue</span> <span class="n">polar</span><span class="p">(</span><span class="n">defaultZ</span><span class="p">,</span> <span class="n">defaultX</span><span class="p">)</span></code></pre></figure>
<p>The default accelerations issued by the monitor aren’t actually
fixed. Instead, the monitor uses the following logic (independently in each
dimension):</p>
<ol>
<li>If the vehicle is moving towards the middle of the bounding box, issue acceleration of <code class="highlighter-rouge">0</code>.</li>
<li>If the vehicle is moving away from the middle of the bounding box, issue acceleration of <code class="highlighter-rouge">amin < 0</code>.</li>
</ol>
<p>There is a more detailed explanation of <code class="highlighter-rouge">amin</code> in
<a href="/quadcopter/test/2015/07/17/fall-at-bounds.html">this post</a>, but for our purposes here, it is any
value less than <code class="highlighter-rouge">0</code>.</p>
<p>Now suppose that the vehicle is stationary (zero velocity) outside the
upper bound in the X dimension. The monitor will issue an acceleration of
<code class="highlighter-rouge">amin</code> in the X dimension, causing the quadcopter to start moving back
towards the middle of the box in the X dimension. However, the next time
the monitor runs (every 0.01 seconds), it will issue an acceleration of 0.</p>
<p>In a world with no unmodeled forces, the vehicle will eventually move back
inside the bounding box, at the velocity induced by accelerating at <code class="highlighter-rouge">amin</code>
for 0.01 seconds. However, we do not live in such a world. In reality, the
quadcopter will be stuck until the wind pushes it back inside the
boundaries, or the default action of the monitor inches it back inside.</p>
<p>Prior to observing the quadcopter getting stuck in a flight test, we didn’t
consider verifying anything about what happens outside the bounding
box. However, this test has shown us that we should. The reality is that
unmodeled forces can always push the system outside its bounds, so the
monitor should behave reasonably in those situations. In a future post,
I’ll talk about the precise property we should verify in order to ensure
this reasonable behavior.</p>
Fri, 07 Aug 2015 11:12:17 +0000
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/08/07/quadcopter-stuck.html
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/08/07/quadcopter-stuck.htmlquadcoptertestImproper quadcopter orientation<p>As I mentioned in <a href="/veridrone/quadcopter/test/2015/07/17/fall-at-bounds.html">this post</a>,
the first test of our rectangular bounding monitor had a few interesting
problems. In order to discover the cause of the problems, we needed to run
more tests. However, running new tests seemed to uncover a new problem: the
monitor had no idea where it was.</p>
<p>More specifically, the rectangular monitor’s estimate of its X position
(how far to the left or right it is) was completely off. This was
surprising to us since our first test revealed no such issue. The
consequence of this problem was that the monitor would cause the quadcopter
to behave in insane ways. Though Sorin had the quadcopter hovering well
within the safe zone, it would all of a sudden roll hard right because it
thought that it was outside the safe zone. Obviously, this was not ideal.</p>
<p>To debug these kinds of issues, we log a bunch of data while the quadcopter
flies. A quick look at the data revealed that the X position estimates the
quadcopter was receiving from the sensors was way off (by as much as 50
meters at times). The monitor doesn’t actually get its data directly from
the sensors. Instead, the raw sensor data is first run through what’s
called sensor fusion. Sensor fusion takes data from a variety of sensors
with different sources of error and computes an estimate of the current
state (position, velocity, etc.) of the vehicle. Our first guess was that
our code had somehow interacted in a weird way with the sensor fusion code.</p>
<p>To test this, we ran the quadcopter again, now logging raw GPS data, both
with and without our monitor code running. From this test, we discovered
two things:</p>
<ol>
<li>The raw GPS coordinates were just as inaccurate as the X position
estimates coming from the sensor fusion code.</li>
<li>The raw GPS coordinates were way off, even when we weren’t running
our monitor code.</li>
</ol>
<p>One possibility was that the GPS unit was damaged from our original
test. However, we were able to rull this out because after the crash in our
original test, we were able to get the vehicle up and running again,
without any GPS issues. Another possibility was that the quadcopter’s
<em>mode</em> was somehow affecting the position estimates. Quadcopter’s like the
IRIS+ that we use often have multiple modes: stabilize, loiter, etc. These
mode essentially dictate how much the autopilot software helps the human
pilot. For example, stabilize just keeps the quadcopter at the pilot’s
desired attitude, but doesn’t try to control position or altitude. Loiter,
on the other hand, does holds the pilot’s desired altitude and position.</p>
<p>For a few reasons that are irrelevant to this post, we had been flying only
in stabilize mode. Since stabilize doesn’t need any position estimates, it
was possible that the software shuts down the GPS unit in order to save
power. I spent a lot of time looking into this possibility (the ardupilot
code on which we build is ~600,000 LOC). I even emailed the ardupilot
developer list to ask whether the mode can affect state estimates, but the
answer was no.</p>
<p>After several more frustrating flight tests, each leaving us more confused
then the last, we finally bothered to look into what exactly the X position
of the quadcopter meant. One key to the monitor working is that rolling
left and right affects the X position of the quadcopter. As it turns out,
the orientation of roll is relative to the initial orientation of the
quadcopter when it is turned on. The X position, on the other hand, has
nothing to do with the initial orientation of the quadcopter, and is
instead fixed to be east/west. In our initial test of the rectangular
bounding monitor, we just happened to initially orient the quadcopter so
that roll corresponded to east/west movement. In subsequent tests, we did
not. The bizarre X position estimates received by the quadcopter were
caused by movement in what we thought was the irrelevant Y direction (but
was in fact the X direction).</p>
<p>It was quite a relief to discover the cause of this issue, though we did
feel a bit silly for not realizing it earlier. Obviously, we now always
make sure that the initial orientation of the quadcopter is correct. And in
the future, we plan to have the monitor take into account the yaw of the
quadcopter so that it won’t matter how we initially orient it. Yet again,
we see that formal verification doesn’t solve all your problems.</p>
Thu, 30 Jul 2015 14:32:17 +0000
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/07/30/quadcopter-orientation.html
http://ucsd-pl.github.io/veridrone/quadcopter/test/2015/07/30/quadcopter-orientation.htmlquadcoptertestAir-traffic control for drones<p>It looks like Google and a bunch of other companies are investigating an
<a href="http://www.bloomberg.com/news/articles/2015-07-24/google-has-way-to-unclog-drone-filled-skies-like-it-did-the-web">air-traffic control system for
drones</a>. It
would be pretty exciting to see “thousands of drones…routinely ply the
skies above cities.” It also opens up an interesting long-term direction
for our project: distributed-systems aspects of UAV safety.</p>
Sat, 25 Jul 2015 16:12:17 +0000
http://ucsd-pl.github.io/veridrone/news/2015/07/25/air-traffic-control.html
http://ucsd-pl.github.io/veridrone/news/2015/07/25/air-traffic-control.htmlnews