Abstract¶
The physics of a task dictate how fast a controller should run to perform it. Balancing, in particular, is a rather lowfrequency task: it was demonstrated (in theory and on real hardware) that a DCMbased controller can balance an adultsize humanoid while running at only 10 Hz. In this talk, we will discuss how this property allows us to write more controlcritical code in a highlevel language like Python. We will illustrate our points with practical submodules from two controllers: the LIPM walking controller for HRP humanoids, implemented with mc_rtc, and the Pink controller for the Upkie wheeledbiped, implemented with Vulp (🦊).
Content¶
Slides  
Python code from the slides  
Motion control software for the Upkie wheeled biped 
Discussion ¶
Thanks to all those who have contributed to the conversation so far. Feel free to leave a reply using the form below, or subscribe to the Discussion's atom feed to stay tuned.

Yuichi Tazaki
Posted on
Does Vulp run on Windows? This would be helpful when setting up a teaching environment for students.

Stéphane
Posted on
Currently Vulp only supports Linux for x86 and ARM architectures, which is Upkie's use case. We can add Windows support to the roadmap: #3.
Note however that Vulp targets mostly researchers, research engineers, or PhD students. It may have some rough edges for students earlier in the learning curve.


Takumi Kamioka
Posted on
This question is about balancing as a lowfrequency task (slide 26 about this work). Isn't this result assuming continuoustime control at some stage?

Stéphane
Posted on
We indeed need to specify what is discretized exactly. In this analysis, the continuoustime DCM dynamics are discretized with sampling time $\def\LdG{\dot{L}_G} \def\Ld{\dot{L}} \def\bfA{\boldsymbol{A}} \def\bfB{\boldsymbol{B}} \def\bfC{\boldsymbol{C}} \def\bfD{\boldsymbol{D}} \def\bfE{\boldsymbol{E}} \def\bfF{\boldsymbol{F}} \def\bfG{\boldsymbol{G}} \def\bfH{\boldsymbol{H}} \def\bfI{\boldsymbol{I}} \def\bfJ{\boldsymbol{J}} \def\bfK{\boldsymbol{K}} \def\bfL{\boldsymbol{L}} \def\bfM{\boldsymbol{M}} \def\bfN{\boldsymbol{N}} \def\bfO{\boldsymbol{O}} \def\bfP{\boldsymbol{P}} \def\bfQ{\boldsymbol{Q}} \def\bfR{\boldsymbol{R}} \def\bfS{\boldsymbol{S}} \def\bfT{\boldsymbol{T}} \def\bfU{\boldsymbol{U}} \def\bfV{\boldsymbol{V}} \def\bfW{\boldsymbol{W}} \def\bfX{\boldsymbol{X}} \def\bfY{\boldsymbol{Y}} \def\bfZ{\boldsymbol{Z}} \def\bfalpha{\boldsymbol{\alpha}} \def\bfa{\boldsymbol{a}} \def\bfbeta{\boldsymbol{\beta}} \def\bfb{\boldsymbol{b}} \def\bfcd{\dot{\bfc}} \def\bfchi{\boldsymbol{\chi}} \def\bfc{\boldsymbol{c}} \def\bfd{\boldsymbol{d}} \def\bfe{\boldsymbol{e}} \def\bff{\boldsymbol{f}} \def\bfgamma{\boldsymbol{\gamma}} \def\bfg{\boldsymbol{g}} \def\bfh{\boldsymbol{h}} \def\bfi{\boldsymbol{i}} \def\bfj{\boldsymbol{j}} \def\bfk{\boldsymbol{k}} \def\bflambda{\boldsymbol{\lambda}} \def\bfl{\boldsymbol{l}} \def\bfm{\boldsymbol{m}} \def\bfn{\boldsymbol{n}} \def\bfomega{\boldsymbol{\omega}} \def\bfone{\boldsymbol{1}} \def\bfo{\boldsymbol{o}} \def\bfpdd{\ddot{\bfp}} \def\bfpd{\dot{\bfp}} \def\bfphi{\boldsymbol{\phi}} \def\bfp{\boldsymbol{p}} \def\bfq{\boldsymbol{q}} \def\bfr{\boldsymbol{r}} \def\bfsigma{\boldsymbol{\sigma}} \def\bfs{\boldsymbol{s}} \def\bftau{\boldsymbol{\tau}} \def\bft{\boldsymbol{t}} \def\bfu{\boldsymbol{u}} \def\bfv{\boldsymbol{v}} \def\bfw{\boldsymbol{w}} \def\bfxi{\boldsymbol{\xi}} \def\bfx{\boldsymbol{x}} \def\bfy{\boldsymbol{y}} \def\bfzero{\boldsymbol{0}} \def\bfz{\boldsymbol{z}} \def\calA{\mathcal{A}} \def\calB{\mathcal{B}} \def\calC{\mathcal{C}} \def\calD{\mathcal{D}} \def\calE{\mathcal{E}} \def\calF{\mathcal{F}} \def\calG{\mathcal{G}} \def\calH{\mathcal{H}} \def\calI{\mathcal{I}} \def\calJ{\mathcal{J}} \def\calK{\mathcal{K}} \def\calL{\mathcal{L}} \def\calM{\mathcal{M}} \def\calN{\mathcal{N}} \def\calO{\mathcal{O}} \def\calP{\mathcal{P}} \def\calQ{\mathcal{Q}} \def\calR{\mathcal{R}} \def\calS{\mathcal{S}} \def\calT{\mathcal{T}} \def\calU{\mathcal{U}} \def\calV{\mathcal{V}} \def\calW{\mathcal{W}} \def\calX{\mathcal{X}} \def\calY{\mathcal{Y}} \def\calZ{\mathcal{Z}} \def\d#1{{\rm d}{#1}} \def\defeq{\stackrel{\mathrm{def}}{=}} \def\dim{\rm dim} \def\p{\boldsymbol{p}} \def\qdd{\ddot{\bfq}} \def\qd{\dot{\bfq}} \def\q{\boldsymbol{q}} \def\xdd{\ddot{x}} \def\xd{\dot{x}} \def\ydd{\ddot{y}} \def\yd{\dot{y}} \def\zdd{\ddot{z}} \def\zd{\dot{z}} \delta t$. Meanwhile, force control is assumed to run in an underlying continuous process, but its outcome (e.g. delay) is lumped in a ZMP tracking error at the next sampling time.

Yuichi Tazaki
Posted on
I have a followup question: does this work consider the distance from the ZMP to the edge of its support area?
It seems the maximum sampling period would become smaller as this distance shrinks.

Stéphane
Posted on
Indeed, this analysis does not consider the distance to the edge of the support area.
I agree with you that the maximum sampling period should be affected by the position of the ZMP within its support area.



Yuki Onishi
Posted on
You mentioned Rust and Julia. Do you have plans to extend Vulp to these languages?

Stéphane
Posted on
Vulp being a protocol, it is open to implementation in either Rust or Julia. Rust has proper safety guarantees compared to C++, and Julia performs faster than Python with roughly the same level of abstraction—although, regarding this last point, we saw in this presentation that the current performance of Python is already sufficient for many balancing use cases. Personally I'm interested in Rust's guarantees and type system.

Yuki Onishi
Posted on
That's good to hear! I contribute to OpenRR, the Open Rust Robotics platform. You should check it out.


