This project is a Django application that aims to be integrated in a ROS2 python package.
Your ROS2 package will then be able to start a node that will export CRUD (Create, Read, Update, Delete) ROS services for Django models.
The goal is to have a way to easily store and retrieve structured data in a ROS2 ecosystem via services, with the power of django framework that includes schema migration, admin interface, field validation, and many more.
For now, install it directly from GitHub
pip install git+https://github.com/robocc/ros2-django.git
Next, create a ROS2 package following the example in example_pkg/
The quickest way is to copy/use example_pkg
in your workspace. It contains a basic django app with 2 ros models and ros2-django configured.
cd example_pkg
colcon build
source install/local_setup.bash # re-source ros workspace
./manage.py migrate # create database
./manage.py createsuperuser # create admin user
./example_pkg_node.py # start ros node
You can also run
./manage.py runserver
and connect to http://localhost:8000 to access admin interface
From now, you can edit your application models.py
and create your model objects.
- All the models you want to export to ROS should inherit from
ros2_django.models.RosModel
- All the fields you want to export to ROS should be from module
ros2_django.fields
(for exampleros2_django.fields.RosCharField
) - A specific
ros2_django.fields.RosMsgField
allows you to store/retrieve directly a ROS message by specifying its type - Custom fields can be created, they should inherit from both
ros2_django.fields.RosFieldMixin
and correspondant Django field
Consider the following example of models.py
from django.db import models
from ros2_django.models import RosModel
import ros2_django.fields
from geometry_msgs.msg import Pose
class Map(RosModel):
id = ros2_django.fields.RosBigAutoField(primary_key=True)
name = ros2_django.fields.RosCharField(max_length=128)
description = ros2_django.fields.RosTextField(blank=True)
webp = ros2_django.fields.RosImageField(blank=True, null=True)
def __str__(self):
return f"[{self.id}] {self.name}"
class PoseOnMap(RosModel):
id = ros2_django.fields.RosBigAutoField(primary_key=True)
name = ros2_django.fields.RosCharField(max_length=128)
pose = ros2_django.fields.RosMsgField(ros_msg=Pose, ros_type="geometry_msgs/Pose")
map = ros2_django.fields.RosForeignKey(
"Map",
on_delete=models.CASCADE,
)
In this example, Map
and PoseOnMap
messages will be created, with all the fields. And the pose
field of PoseOnMap
will be a standard geometry_msgs/Pose
.
This will be done automatically with the example CMakeLists.txt
Once models.py
is written, you can create your database with standard ./manage.py makemigrations
and ./manage.py migrate
.
You now also can run ./manage.py gen_ros_msgs example_app out/
that will generate all messages and services definition for ROS in directory out/
.
For each RosModel
named Foo
, it will generate:
Foo.msg
with all its fields, including reverse relationsFooRaw.msg
with all its fields without relations (not generated if its the same asFoo.msg
)GetFoo.srv
andGetFooRaw.srv
services to get aFoo
orFooRaw
via its idSetFoo.srv
andSetFooRaw.srv
to create/set aFoo
orFooRaw
object via its id (creation withid=-1
)ListFoo.srv
andListFooRaw.srv
to list allFoo
orFooRaw
DeleteFoo.srv
andDeleteFooRaw.srv
to remove aFoo
orFooRaw
via its id
Theses services will be implemented and served via ros2_django.ros_node.ROS2DjangoNode
Example interfaces for previous model example:
Map.msg
:
PoseOnMap[] poseonmaps
int64 id
string name
string description
uint8[] webp
MapRaw.msg
:
int64 id
string name
string description
uint8[] webp
PoseOnMap.msg
:
int64 id
string name
geometry_msgs/Pose pose
int64 id_map
GetMap.srv
:
int64 id
---
bool success
string message
Map map
SetMapRaw.srv
:
MapRaw map
---
bool success
string message
int64 id
ListMap.srv
:
---
bool success
string message
Map[] maps
In order to customize interface services, one can declare a RosMeta
class inside model.
Current accepted attributes are:
ros_search
: list of fields (or fields tuple for and search) that we can search upon forGet
andSet
services.- Following example will generate a
GetFooByName
andSetFooByName
service
Generatedclass Foo(RosModel): id = ros2_django.fields.RosBigAutoField(primary_key=True) name = ros2_django.fields.RosCharField(max_length=128) class RosMeta: ros_search = ['name']
GetFooByName.srv
string name --- bool success string message Foo foo
- Following example will generate a
ros_filter
: list of fields to filter upon forList
services- Following example will generate a
ListBarByFoo
to list allBar
that depends on a specificFoo
Generatedclass Bar(RosModel): id = ros2_django.fields.RosBigAutoField(primary_key=True) foo = ros2_django.fields.RosForeignKey( "Foo", on_delete=models.CASCADE, ) class RosMeta: ros_filter = ['foo']
ListBarByFoo.srv
int64 id_foo --- bool success string message Bar[] bars
- Following example will generate a
ros_ignore_fields
: list of fields not included in the final messageros_thin_fields
: if present, will restrict which fields will be filled inList
services to gain bandwidth
example_pkg
folder contains a ready-to-use package using ros2-django
. These instructions are for people who want to do it manually
- Create a ROS2 package with a
CMakeLists.txt
(not a Python package)ros2 pkg create --build-type ament_cmake example_pkg
- Create a standard Django project and app in your package
django-admin startproject example_django_project .
./manage.py startapp example_app
- In
example_django_project/settings.py
- Add
ros2_django
inINSTALLED_APPS
- Set
ROS_INTERFACES_MODULE_NAME
to ROS package name (example_pkg
in this case)
- Add
- Setup routes/admin stuff for Django if you want (cf. django documentation)
- Edit your
CMakeLists.txt
to generate interfaces and install python packages (cf. example) - Create a node starter python file that will spin
ros2_django.ros_node.ROS2DjangoNode
(cf. example)