ROS 2 humble + Docker + QEMU

Build ROS 2 and its dependencies

git clone https://github.com/Wind-River/vxworks7-ros2-build.git
cd vxworks7-ros2-build

Build Docker image

docker build --no-cache -t vxbuild:22.04 Docker/22.04/vxbuild/.
docker build --no-cache -t vxros2build:humble Docker/22.04/vxros2build/.

Download and extract the VxWorks SDK

The 23.09 SDK for IA - QEMU x86_64 shall be used from https://forums.windriver.com/t/vxworks-software-development-kit-sdk/43

cd ~
wget https://d13321s3lxgewa.cloudfront.net/wrsdk-vxworks7-qemu-1.13.2.tar.bz2
mkdir ~/wrsdk && cd ~/wrsdk
tar -jxvf ~/wrsdk-vxworks7-qemu-1.13.2.tar.bz2 --strip 1
rm wrsdk-vxworks7-qemu-1.13.2.tar.bz2 

Run Docker image

cd vxworks7-ros2-build
docker run -ti -h vxros2 -v ~/wrsdk:/wrsdk -v $PWD:/work vxros2build:humble

By default it runs as a user wruser with uid=1000(wruser) gid=1000(wruser), if you have different ids, run it as

$ docker run -ti -h vxros2 -e UID=`id -u` -e GID=`id -g` -v ~/wrsdk:/wrsdk -v $PWD:/work vxros2build:humble

Start build

Inside the Docker container: check the ROS_DISTRO version, source the development environment and start build

wruser@vxros2:/work$ source /wrsdk/sdkenv.sh
wruser@vxros2:/work$ make info
wruser@vxros2:/work make

Build artifacts are in the export directory

wruser@vxros2:/work$ ls output/export/deploy/
bin  lib  share  vxscript

Rebuild from scratch

wruser@vxros2:/work make distclean
wruser@vxros2:/work make
wruser@vxros2:/work exit

Run ROS 2 examples

QEMU is used to boot VxWorks and run Python and C++ ROS 2 examples, for that tap0 interface shall be configured.

sudo apt-get install uml-utilities
sudo tunctl -u $USER -t tap0
sudo ifconfig tap0 192.168.200.254 up

VxWorks is tested with

$ sudo apt-get install qemu-system
$ qemu-system-x86_64 --version
QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.2)
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers

Create an HDD image

Run QEMU with a prebuilt VxWorks kernel and a created HDD image.

# create a disk 2048MB
$ dd if=/dev/zero of=./output/ros2.img count=2048 bs=1M
# format it as a FAT32
$ mkfs.vfat -F 32 ./output/ros2.img

# mount, copy, unmount, you need to be `sudo`
$ mkdir -p ~/tmp/mount
$ sudo mount -o loop -t vfat ./output/ros2.img ~/tmp/mount
$ sudo cp -r -L ./output/export/deploy/* ~/tmp/mount/.
$ sudo umount ~/tmp/mount
sudo qemu-system-x86_64 -m 2G -kernel ~/wrsdk/vxsdk/bsps/*/vxWorks \
-net nic -net tap,ifname=tap0,script=no,downscript=no -display none -serial stdio \
-append "bootline:fs(0,0)host:/vxWorks h=192.168.200.254 e=192.168.200.1 g=192.168.200.254 u=ftp pw=ftp123 o=gei0 s=/ata4/vxscript" \
-device ich9-ahci,id=ahci -drive file=./output/ros2.img,if=none,id=ros2disk,format=raw -device ide-hd,drive=ros2disk,bus=ahci.0

The HDD image will be mounted inside VxWorks under the /usr directory

-> ls "/usr"
/usr/bin
/usr/lib
/usr/share
/usr/vxscript

Telnet to the VxWorks QEMU target

telnet 192.168.200.1

Run ROS 2 C++ examples

It is possible to run examples by directly invoking binaries

-> cmd
[vxWorks *]# /usr/lib/examples_rclcpp_minimal_timer/timer_lambda
Launching process 'timer_lambda' ...
Process 'timer_lambda' (process Id = 0xffff80000046f070) launched.
[INFO] [minimal_timer]: Hello, world!
[INFO] [minimal_timer]: Hello, world!

Or by using the ros2cli interface

-> cmd
[vxWorks *]# python3 ros2 run examples_rclcpp_minimal_timer timer_lambda
Launching process 'python3' ...
Process 'python3' (process Id = 0xffff800008268ac0) launched.
[INFO] [minimal_timer]: Hello, world!
[INFO] [minimal_timer]: Hello, world!

Run ROS 2 Python examples

It is possible to run examples by directly invoking Python scripts. First, figure out the full path.

// Some code[vxWorks *]# python3 ros2 pkg executables --full-path demo_nodes_py
 Launching process 'python3' ...
 Process 'python3' (process Id = 0xffff80000046f070) launched.
/usr/lib/demo_nodes_py/add_two_ints_client
/usr/lib/demo_nodes_py/add_two_ints_client_async
/usr/lib/demo_nodes_py/add_two_ints_server
/usr/lib/demo_nodes_py/listener
/usr/lib/demo_nodes_py/listener_qos
/usr/lib/demo_nodes_py/listener_serialized
/usr/lib/demo_nodes_py/talker
/usr/lib/demo_nodes_py/talker_qos

Then invoke the Python interpreter and pass the script as a parameter

[vxWorks *]# python3 /usr/lib/demo_nodes_py/talker
[INFO] [talker]: Publishing: "Hello World: 0"
[INFO] [talker]: Publishing: "Hello World: 1"

Native ROS 2 compilation

Native ROS 2 is used mostly for fast prototyping during ROS 2 development. Use the same docker image for that.

$ cd vxworks7-ros2-build
$ docker run -ti -h ros2native -v $PWD:/work vxros2build:humble
wruser@ros2native:/work$ mkdir -p ros2_native/src && cd ros2_native
wruser@ros2native:/work/ros2_native$ vcs import src < /work/build/ros2/ros2_ws/ros2.repos
wruser@ros2native:/work/ros2_native$ colcon build --merge-install --cmake-force-configure --packages-up-to-regex examples_rcl* ros2action ros2component ros2node ros2pkg ros2service ros2topic ros2cli ros2lifecycle ros2multicast ros2param ros2run demo_* dummy_robot launch --cmake-args -DCMAKE_BUILD_TYPE:STRING=Debug -DBUILD_TESTING:BOOL=OFF

wruser@ros2native:/work/ros2_native/install$ source setup.bash
wruser@ros2native:/work/ros2_native/install$ ros2 run demo_nodes_py talker
[INFO] [talker]: Publishing: "Hello World: 0"
[INFO] [talker]: Publishing: "Hello World: 1"

VxWorks ROS 2 development

The following example shows how to develop and run ROS 2 package called my_package under VxWorks. It is recommended to prototype it first under the native ROS 2 build environment that contains the same ROS 2 version as a VxWorks one. After the package is developed and tested, it can be copied and compiled under VxWorks ROS 2 build environment.

Step 1: create and test a new ROS 2 package under the native ROS 2 build environment

  1. Follow the procedure of how to prepare native ROS 2 build environment.

  2. Create a new ROS 2 package

wruser@ros2native:/work$ cd ros2_native/src
wruser@ros2native:/work/ros2_native/src$ source ../install/setup.bash
wruser@ros2native:/work/ros2_native/src$ ros2 pkg create --build-type ament_cmake my_package
going to create a new package
package name: my_package
destination directory: /work/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['wruser <wruser@todo.todo>']
licenses: ['TODO: License declaration']
build type: ament_cmake
dependencies: []
creating folder ./my_package
creating ./my_package/package.xml
creating source and include folder
creating folder ./my_package/src
creating folder ./my_package/include/my_package
creating ./my_package/CMakeLists.txt
  1. Create my_package.cpp file

wruser@ros2native:/work/ros2_native/src$ cd my_package/src
wruser@ros2native:/work/ros2_native/src/my_package/src$ cat > my_package.cpp <<EOF
#include <iostream>

using namespace std;

int main(int argc, char * argv[])
{
  cout << "Hello World!" << endl;
  return 0;
}
EOF
  1. Modify CMakeLists.txt to build my_package

Add the following lines to the CMakeLists.txt before if(BUILD_TESTING)

add_executable(my_package src/my_package.cpp)

install(TARGETS
  my_package
  DESTINATION lib/${PROJECT_NAME}
)

You can use sed for it.

wruser@ros2native:/work/ros2_native/src/my_package$ sed -i '/find_package(<depen/aadd_executable(my_package src/my_package.cpp)\ninstall(TARGETS\n  my_package\n  DESTINATION lib/\${PROJECT_NAME}\n)' CMakeLists.txt
  1. Build my_package

wruser@ros2native:/work/ros2_native/src/my_package$ cd ../..
wruser@ros2native:/work/ros2_native$ colcon build --merge-install --packages-up-to my_package
  1. Run my_package

wruser@ros2native:/work/ros2_native$ source install/setup.bash
wruser@ros2native:/work/ros2_native/install$ ros2 run my_package my_package
Hello World!

Step 2: Build and run a new ROS 2 package under VxWorks ROS 2 build environment

  1. Start docker and copy my_package to the VxWorks ros2_ws workspace

$ docker run -ti -h vxros2 -v ~/Downloads/wrsdk:/wrsdk -v $PWD:/work vxros2build:humble
wruser@vxros2:/work$ cp -r ros2_native/src/my_package build/ros2/ros2_ws/src/.
  1. Rebuild ROS 2 with my_package

wruser@vxros2:/work$ source /wrsdk/sdkenv.sh
wruser@vxros2:/work$ rm /work/build/.stamp/ros2.build
wruser@vxros2:/work$ PKG_PKGS_UP_TO=my_package DEFAULT_BUILD=ros2 make
wruser@vxros2:/work$ exit
  1. Create ros2.img as described here and start QEMU

$ sudo qemu-system-x86_64 -m 2G -kernel ~/Downloads/wrsdk/vxsdk/bsps/*/vxWorks \
-net nic -net tap,ifname=tap0,script=no,downscript=no -display none -serial stdio \
-append "bootline:fs(0,0)host:/vxWorks h=192.168.200.254 e=192.168.200.1 g=192.168.200.254 u=ftp pw=ftp123 o=gei0 s=/ata4/vxscript" \
-device ich9-ahci,id=ahci -drive file=./output/ros2.img,if=none,id=ros2disk,format=raw -device ide-hd,drive=ros2disk,bus=ahci.0
  1. Setup environment variables and run my_package

-> cmd
[vxWorks *]# python3 ros2 run my_package my_package
Launching process 'python3' ...
Process 'python3' (process Id = 0xffff80000036cb10) launched.
Hello World!

Last updated