ROSCon
2018
sep 30
1/38

RCLAda:the Ada client libraryfor ROS2
A.R. Mosteo, D. Tardioli (CUD)
F. Chouteau, Y. Moy (Adacore)
2018-sep-30

ROSCon2018
sep 302/38• About us• Motivation– Why ROS2
– Why Ada

CONTENTS• Architecture– Packages– CMake helpers• Examples

Yet anotherlanguage?

ROSCon2018
sep 30Research group

3/38

Robotics, Perception and Real-Time group - RoPeRT
University of Zaragoza
Engineering Research Institute of Aragon

ROSCon2018
sep 304/38

Optimaldistributed
coordination

Real-time
multi-hopcommunication

RoPeRT

http://robots.unizar.es/

Undergrounddrone
reconnaissanceROSCon2018
sep 30Industrial partner

5/38

https://www.adacore.com/industries

GNU NYU Ada Translator / FSF GNAT-GCC / SPARKROSCon2018
sep 30In a nutshell

6/38

We can now write ROS2
nodes in Ada 2012

Ada coin author: Steven Craeynest

ROSCon2018
sep 30
7/38➔ 1975: Working group DoD / UK MoD● STRAWMAN first discussions➔ 1978: STEELMAN requirements ● Embedded, reliability, maintainability, efficiency requirements● No suitable existing candidate➔ 1979: Green proposal by Jean Ichbiah of Honeywell BullAda history● Renamed to Ada➔ 1983: standard ANSI/MIL-STD-1815A (Ada 83)➔ 1991-1997: DoD mandate years● From 450 to 37 languages by 1998

➔ Today: niche in many critical industries

AdaRustC++

PythonJava

ROSCon2018
sep 308/38• Structured–

Separate specifications• Strongly, statically typed–

Named types (even pointers)• Imperative (Pascal-like)–

Object oriented, optionally• High-level concurrency–

Tasks, Rendezvous, Monitors

Hello, Ada• Design-by-contract––Pre-, post-conditions
Type, loop invariants• Comparable in purpose to C++–Emphasis in• Maintainability• Correctness• Early error detection

with Ada.Text_IO; use Ada.Text_IO;
procedure Hello isbegin
Put_Line ("Hello, ROSCon!");end Hello;type Speeds is new Float;
type Lengths is new Float;
spd : Speeds := 0.0;
len : Lengths := spd; -- Bzzzt
procedure Inc (X : in out Integer)
with Pre => X < Integer’Last;
type Prime is new Positive
with Predicate =>(for all D in 2 .. Prime / 2 =>Prime mod D /= 0);

ROSCon2018
sep 30Ada numeric types

9/38type Robot_ID is new Natural;-- Type compatibility is by nametype Task_ID is new Natural;-type Distance is range 0 .. 1_000_000_000; -- Explicit bounds
type Coordinate is range -180.0 .. 180.0;-- Floating point with range

type Probability is digits 5
range 0.0 .. 1.0;-- Floating point with-- minimum guaranteed precision

type Laser_Readings is delta 10.0 / 2**8
range 0.0 .. 10.0;-- Binary fixed pointtype Euros is delta 0.001 digits 12;-- Decimal fixed pointtype Weekdays is (Monday, Tuesday, Wednesday, Thursday, Friday);type Escaped_Robot_Counter is array (Weekdays) of Natural
with Default_Component_Value => 0; -- Arbitrarily indexed arrays

ROSCon2018
sep 3010/38• Ada Rapporteur Group––Receives suggestions, requests, comments
Prioritizes “not” doable in current Ada• Ada Reference Manual (ARM)––

AARM: Annotated ARM for experts, compiler writers
All are ISO standards

Evolution• Ada Conformity Assessment Test Suite (ACATS)
FeaturePackages
Generics
Derived ADTs
Object orientation (tagged types)
Multiple inheritance (abstract interfaces)
Design by Contract
Numeric types (fixed, floating, decimal, custom)
Tasks
Monitors
Real-time systems annex
Ravenscar profile
Multiprocessor affinities, Multiprocessor Ravenscar
Parallel constructs (blocks, loops)

Ada 83✓✓✓

Ada 95✓✓✓✓

Ada 2005
✓
✓
✓
✓✓

✓
✓

✓
✓
✓
✓

✓
✓
✓
✓
✓

Ada 2012
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓

Ada 202X
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓
✓

Source: https://www.adacore.com/about-ada

ROSCon2018
sep 30
11/38

ROS2• Emphasis on– Embedded– Real-timeWhy ROS2

• Traditional strong points of Ada– Annex C: systems programming• Interrupts, atomics, volatiles, task identification– Annex D: (hard) real time
• Priorities, schedulers, monotonic clock, RAVENSCAR– Ada Reference Manual (ISO/IEC 8652:2012)– Industries requiring certification (aero but… autonomous robots?)•

Related: SPARK Ada subset for formal proofs on code

ROSCon2018
sep 3012/38

user code

ROS2 client supportclient libraries
rclcpprclpy

ROS2 client API (rcl)
ROS2 middleware (rmw)
DDS implementations
FastRTPS

Connext

Original diagram by Deanna Hood, William Woodall: https://goo.gl/oCHR7HROSCon
2018
sep 3013/38

user code

ROS2 client supportclient libraries
rclcpprclpy

ROS2 client API (rcl)
ROS2 middleware (rmw)
DDS implementations
FastRTPS

Connext

threading model

intra-process commnamespaces

timeparameters

console logging

ROSCon2018
sep 3014/38

user code

Ada user code

ROS2 client supportclient libraries
rclcpprclpy

ROS2 client API (rcl)
ROS2 middleware (rmw)
DDS implementations
FastRTPS

Connext

rcladaROSCon2018
sep 30
15/38

ROS2 architecture II

Ada user codercladarcl

interface description language(rosidl)

ROSCon2018
sep 30
16/38

ROS2 architecture II

Ada user codercladarcl

interface description language(rosidl)threading model

intra-process commROSCon
2018
sep 3017/38

rclada_client_skeleton

rclada_examples

Actual packages

rcladarosidl_generator_ada

rosidl_generator_crosidl_typesupport_interface
rosidl_typesupport_introspection

rclada_common

gprbuild

rcl

rmw

gnat
Ada ROS2 packages
Ada regular tools
ROS2 packages

ROSCon2018
sep 3018/38

rclada_client_skeleton

Ada ROS2 packages

rclada_examples
rcladaEmpty quickstart package

Usual suspects (talker, listener, add_two_ints, …)

Client facilities (nodes, topics, services, …) & self-tests

rosidl_generator_ada

Message supportcompile↓rclada_common

Ada packages
Ada tools

gprbuild
gnat

CMake functions
Ada build system(ARM ch.10)

bind↓
link

Ada compiler (gnat-gcc) and tools

Ada binder ≈ CMake● Check unit consistency○ No outdated *.o● Find safe elaboration order○ No “init order fiasco”● Generate main procedure○ Perform initialization○ Call user’s main
○ Perform cleanupROSCon
2018
sep 3019/38

Specification (*.ads)

Implementation (*.adb)

package RCL.Logging is

package body RCL.Logging is

procedure Initialize;-- public methods

procedure Initialize is
begin-- do whatever has to be done
end Initialize;

private-- private methods

Ada packages-- protected methods-- 1-pass compiler needed info

end RCL.Logging;end RCL.Logging;Main procedure (*.adb)
with RCL.Logging;procedure RCL.Talker is
begin
Logging.Initialize;
end RCL.Talker;

ROSCon2018
sep 30
20/38• Writing bindings:

Writing Ada bindings– Manual writing
No need to be exhaustive
High quality (thick binding)More effort
May become de-sync’d– Automated generation“Less” work
Completeness
Assured consistency
Lower quality (thin binding)
Might not compile• Ada/GNAT support:– Annex B: interface to other languages•

C/C++, Fortran, Cobol– gcc -fdump-ada-spec file.h/* C prototype */int initialize(options_t *opts,char *argv[]);-- Ada automatic binding
function Initialize(opts : access Options_T;
argv : System.Address)
return Interfaces.C.int
with Import, Convention => C;-- Ada manual binding
type Arg_Array isarray (Natural range <>) of aliased
Interfaces.C.Strings.Chars_Ptr
with Convention => C;function Initialize(opts : in out Options_T;
argv :Arg_Array)
return Interfaces.C.int
with Import, Convention => C;

ROSCon2018
sep 30

RCLAda: leverage colcon for best of both worlds

21/38

with RCL.Nodes;
procedure My_Shiny_Node ispackage RCL is …
-- And children RCL.*-- Manually written

Ada packages

rcladapackage rcl_allocator_h is …
package rcl_client_h is …
package rcl_node_h is …
package rcl_*_h is …
-- generated on first colcon build
package ROSIDL is …
-- And children ROSIDL.*-- Manually written

rosidl_generator_ada

package builtin_interfaces_*_h is …
package rosidl_*_h is …
package std_msgs_*_h is …
-- generated on first colcon build

ROSCon2018
sep 3022/38

rcladarosidl_generator_ada● Messages:○ ROSIDL.Dynamic: Complete

Completion level○ ROSIDL.Typesupport: Complete

ROSCon2018
sep 30

Message fields

23/38

rosidl_generator_ada
declareSupport : ROSIDL.Typesupport.Message_Support :=ROSIDL.Typesupport.Get_Message_Support(Pkg_Name, Msg_Type);
Msg : ROSIDL.Dynamic.Message := Init (Support);
begin
Msg (“valid”).As_Bool := True;
Msg (“X”).As_Float32 := 1.0;-- Individual values
Msg (“Values”).As_Array (42).As_Int8 := 0;-- Array indexingMsg (“Image”).As_Matrix ((100, 50, 1)).As_Int8 := 0;-- Matrix indexing
end;

Obtain message type

Reference to fields- No data copy
1D vector indexing- Bounds checkedMatrix indexing- Tuple of indices

ROSCon2018
sep 30Support for colcon_cmake24/38• ada_begin_package()rclada_common• ada_end_package()
Needed to propagate Ada information through ROS2 packages• ada_add_executables(TARGET SRCDIR DSTDIR EXECUTABLES)
Declares an Ada executable to be built and exported (tab completion)• ada_add_library(TARGET SRCDIR GPRFILE)
Declares an Ada library project to be built and exported to other Ada packages• ada_import_msgs(PKG_NAME)
Generates bindings to the typesupport handle functions
Could disappear once RCLAda is integrated in build farm• ada_generate_binding(TARGET SRCDIR GPRFILE INCLUDE)
Invokes the binding generator in the context of an Ada projectROSCon2018
sep 30Talker

25/38

procedure Talker is
Support : constant ROSIDL.Typesupport.Message_Support :=ROSIDL.Typesupport.Get_Message_Support ("std_msgs", "String");
Node: Nodes.Node:= Nodes.Init (Utils.Command_Name);
Pub: Publishers.Publisher := Node.Publish (Support, "/chatter");task Publisher;task body Publisher isCount : Positive:= 1;
Period : constant Duration:= 1.0;
Next : Calendar.Time:= Calendar.Clock;
Msg: ROSIDL.Dynamic.Message := ROSIDL.Dynamic.Init (Support);
beginloop
Msg ("data").Set_String ("Hello World:" & Count'Img);
delay until Next;
Pub.Publish (Msg);
Counter := Count + 1;
Next:= Next + Period; -- Next := @ + Period; -- in Ada 202x
end loop;end Publisher;
begin
Node.Spin (Until => Forever);end Talker;

Dynamic handle retrieval
Node initialization in the stack
Topic creation
An Ada task without sync entries

Duration is a built-in Ada typeMessage allocation
Message fields are- indexed by name- type checked- bounds checked
Delay without drift
Spin forever (named parameter)(note: all Ada tasks have masters
that await their completion)

ROSCon2018
sep 3026/38

Callback definition
procedure Listener isprocedure Callback (Node : in out Nodes.Node'Class;Msg : in out ROSIDL.Dynamic.Message;
Info :ROSIDL.Message_Info) is
begin
Logging.Info ("Got chatter: '" & Msg ("data").Get_String & "'");
end Callback;Node : Nodes.Node := Nodes.Init ("listener");

Listener

begin
Node.Subscribe(ROSIDL.Typesupport.Get_Message_Support ("std_msgs", "String"),"/chatter",
Callback'Access);
Node.Spin (Until => Forever);end Listener;

Standard ROS2 Logging

Ada String (not null-terminated)
Register callback- Using procedure address

LISTENERROSCon2018
sep 3027/38

procedure Server is-- Omitted declarations

Services

procedure Adder(Node : in out Nodes.Node'Class;
Req :ROSIDL.Dynamic.Message;
Resp : in out ROSIDL.Dynamic.Message)
isA : constant ROSIDL.Int64 := Req ("a").As_Int64;
B : constant ROSIDL.Int64 := Req ("b").As_Int64;
begin
Resp ("sum").As_Int64 := A + B;
end Adder;
begin
Node.Serve(ROSIDL.Typesupport.Get_Service_Support("example_interfaces", "AddTwoInts"),"add_two_ints",
Adder'Access);
end Server;

SERVERprocedure Client is -- Synchronous version-- Omitted declarations
Request : ROSIDL.Dynamic.Message := … ;begin
Request ("a").As_Int64 := 2;
Request ("b").As_Int64 := 3;
declareResponse : constant ROSIDL.Dynamic.Message :=Node.Client_Call (Support,"add_two_ints",Request);
begin
Logging.Info ("Got answer:" &Response ("sum").As_Int64.Image);end;
end Client;

Blocking call

CLIENTROSCon
2018
sep 3028/38Everything on the stack: Ada indefinite types
declaretype Int_Array is array (Positive range <>)of Integer;

Stack management

Arr : Int_Array (1 .. 100);
Hello : constant String := “Hello”;
Other_Arr : Int_Array (1 .. Get_Elsewhere);begin-- Variable stack use so measure it or limit it!
end;

Indefinite type (unknown size at compile time)- but definite values! (known size at runtime)
Constrained by declaration with static size
Constrained by initialization with static size
Constrained by declaration with unknown size
- at compile time

declaretype Unconstrained (Length : Natural) is record
Name : String (1 .. Length);end record;
U1 : constant Unconstrained := Get_Unconstrained;
U2 :Unconstrained (10);beginConstrained by initialization with unknown size
Constrained by declaration with static size

ROSCon2018
sep 30Indefinite concurrent executor type
Parent abstract type in RCL.Executors

29/38

package RCL.Executors.Concurrent isConcurrent executors

type Runner_Pool is array (Positive range <>) of Runner;-- Runner task type declaration omittedtype Executor (Max_Nodes : Count_Type :=Default_Nodes_Per_Executor;
Queue_Size : Count_Type :=Count_Type (System.Multiprocessors.Number_Of_CPUs) * 32;
Threads: Positive :=Positive (System.Multiprocessors.Number_Of_CPUs);
Priority: System.Priority :=System.Max_Priority) is
new Executors.Executor (Max_Nodes) withrecord
Pool : Runner_Pool (1 .. Threads);
Queue : Queues.Queue (Capacity => Queue_Size,Ceiling => Priority);
Started : Boolean := False;end record;end RCL.Executors.Concurrent;

Task pool typeExecutor type with discriminants- Nodes supported- Queue size
- Threads- Priority

System.* defined in ARM
OO derivation syntax
Members constrained by discriminants- Standard Ada bounded queues- All Ada bounded containers arestack based
See rclada_test_multicore.adb- One producer- Pooled consumers

ROSCon2018
sep 30
30/38

ROS2 allocators ⇔ Ada storage pools•

Allocators•

Ada defines Storage_Pool type for different:– memory areas (typical in some small boards) (associated to pointer types)– allocation policies (including user-defined)
ROS2 allocators mapped into Ada storage pools– transparent use in Ada programs– immediate testing of RCLAda & ROS2 use of allocators via GNAT.Debug_Pools$ rclada_test_allocators 1
Total allocated bytes:
2335
Total logically deallocated bytes: 2335
Total physically deallocated bytes: 0
Current Water Mark:
0High Water Mark:
415

type Int_Ptr is access Integer -- named pointer typewith Storage_Pool => Debug_Pool;$ rclada_test_allocators 4
Total allocated bytes:
8095
Total logically deallocated bytes: 8095
Total physically deallocated bytes: 0
Current Water Mark:0High Water Mark:
415

pragma No_Allocators;
pragma No_Implicit_Heap_Allocations;pragma No_Standard_Allocators_After_Elaboration;
pragma No_Standard_Storage_Pools;-- See Restrictions in GNAT manual for many more

type Node_Access is access all RCL.Nodes.Node’Class
with Storage_Size => 0;-- No heap allocations

ROSCon2018
sep 3031/38

typedef struct rcutils_allocator_t{
void * (*allocate)(size_t size,void * state);
void (* deallocate)(void * pointer,void * state);

Allocator details

void * (*reallocate)(void * pointer,size_t size,
void * state);
void * (*zero_allocate)(size_t number_of_elements,size_t size_of_element,void * state);
void * state;} rcutils_allocator_t;

package System.Storage_Pools istype Root_Storage_Pool is tagged private;procedure Allocate(Pool: in out Root_Storage_Pool;
Storage_Address:out Address;
Size_In_Storage_Elements : inStorage_Count;
Alignment: in
Storage_Count)
is abstract;
procedure Deallocate(Pool: in out Root_Storage_Pool;
Storage_Address: inAddress;
Size_In_Storage_Elements : inStorage_Count;
Alignment: in
Storage_Count)
is abstract;

Pool : aliased GNAT.Debug_Pools.Debug_Pool;-- Ada pool, compiler provided
Alloc : aliased RCL.Allocators.Allocator (Pool’Access);-- ROS2 allocator, wrapping Ada pool
Node :RCL.Node := Node.Init(Options => (Allocator => Alloc’Access)); -- Set node allocatorROSCon2018
sep 3032/38• SPARK–

Subset of Ada••––

Same compiler
Extra tools for verification/proofs

Historically: special comments about code
Since Ada 2012/SPARK 2014: Ada contracts•procedure Increment (X : in out Integer)
with Global => null,Depends => (X => X),Pre=> X < Integer'Last,Post=> X = X'Old + 1;

Checked by the compiler

SPARK highlights• Can prove:–

Absence of runtime errors (exceptions)•–Runtime checks can be safely disabled

Properties of the program•

Guided by the programmer• Similar in some respects to Frama-C––

But Ada has fewer undefined behaviors
The SPARK subset grows with each version• If interested:––

Take a free book!
Drop by comp.lang.ada (yes, NNTP)

type Prime is new Positive
with Dynamic_Predicate =>(for all D in 2 .. Prime / 2 =>Prime mod D /= 0);

ROSCon2018
sep 30SPARK success stories

33/38

CubeSat from Vermont Tech
http://www.cubesatlab.org-Three years of flight time (2013-2016)
Others: 1x4month, 2x<1week, 8xUnheard of

SND navigation algorithm
https://github.com/riveras/spark-navigation-IROS 2014 paper on errors in robotic navigation
algorithms
SND reimplemented in SPARK
Proven without runtime errors
Possible target to integrate with RCLAda

IRONSIDES DNS server
https://ironsides.martincarlisle.com/
Proven free of (among others):- Buffer overflows- Integer overflows- Information leaks- Race conditions

Tokeneer ID station project (NSA)
https://www.adacore.com/tokeneer
Not only SPARK but full development methodology- Formal language (Z) for specification- ~10KLOC- 4 defects since delivery

ROSCon2018
sep 3034/38

DISTINGUISHING FEATURES• No heap allocations (in RCL)
– Guaranteed by language restrictions & libraries• Relies on automatic low-level binding– Early detection of mismatches on ROS2 API changes

Conclusion• Language ingrained in safety/HRT culture––––Enforced safe program initialization / task completionStrong static type system (incl. numerics) (plus predicates)
A convenient path to formal verification with SPARKSPARK is compiled with the same Ada toolset• Strong backwards & cross-platform compatibility

ROSCon2018
sep 3035/38


✉


THANKS FOR YOUR ATTENTION
https://github.com/ada-ros/ada4ros2/
amosteo@unizar.es@mosteobotic
Acknowledgements:
Dirk Thomas
William Woodall
Esteve Fernandez
ROS answers?