12. Building the Canadarm demo
In this tutorial, we’ll add a cFS app to control the canadarm. The user will publish a ROS2 topic with a mode (0, 1 or 2) and the canadarm on the flight side will move to a open, close or random joint configuration. We’ll use the BRASH bridge to transmit information and will show how to use the canadarm_app code to parse the mode into a joint state to be sent to the robot.
To follow this tutorial, you have to build docker images of BRASH with SpaceROS (see tutorial: Building BRASH Docker images with SpaceROS). Below there is a simplified diagram of the software setup:
12.1. Add canadarm_app to mission
Clone We already have the code for the canadarm app, so clone it in the /apps folder:
$ cd ${HOME}/brash_demonstrations/cFS/apps $ git clone git@github.com:traclabs/canadarm_app.git
We now need to integrate the code to the cFS executable with the next steps:
Scheduling : In
cFS/sample_defs/tables/sch_lab_table.c
, around line 33 :#include "robot_sim_msgids.h" #include "rover_app_msgids.h" + #include "canadarm_app_msgids.h" #include "sbn_msgids.h"
and around line 72 add the 2 lines:
{CFE_SB_MSGID_WRAP_VALUE(ROVER_APP_SEND_HK_MID), 100}, {CFE_SB_MSGID_WRAP_VALUE(ROVER_APP_HR_CONTROL_MID), 10}, + {CFE_SB_MSGID_WRAP_VALUE(CANADARM_APP_SEND_HK_MID), 100}, + {CFE_SB_MSGID_WRAP_VALUE(CANADARM_APP_HR_CONTROL_MID), 10},
These 2 lines are telling the Scheduling app to send these 2 messages (SEND_HK_MID and HR_CONTROL_MID) at rates of 100 and 10 ticks per second, respectively. Since the tick rate is 10 ms, this means that the messages will be sent at a rate of 1Hz and 10Hz.
Telemetry : In file
cFS/sample_defs/tables/to_lab_sub.c
, add around line 41:#include "robot_sim_msgids.h" #include "rover_app_msgids.h" + #include "canadarm_app_msgids.h"
and around line 66:
{CFE_SB_MSGID_WRAP_VALUE(ROVER_APP_HK_TLM_MID), {0, 0}, 4}, + {CFE_SB_MSGID_WRAP_VALUE(CANADARM_APP_HK_TLM_MID), {0, 0}, 4}, {CFE_SB_MSGID_WRAP_VALUE(SNTP_HK_TLM_MID), {0, 0}, 4},
This line is adding the telemetry data message for the canadarm (identified with ID HK_TLM_MID) to the to_lab.
Build Add the target canadarm_app as a target in line 89 of
cFS/sample_defs/targets.cmake
(append the end of the list) :list(APPEND MISSION_GLOBAL_APPLIST ros_app sbn sbn_udp sbn_f_remap cf robot_sim rover_app + sntp canadarm_app)
Link The step above will build a .so file. We’ll need our cFS executable to link to it. Go to
cFS/sample_defs/cpu2_cfe_es_startup.scr
and add the line for this app as shown below:CFE_APP, rover_app, RoverAppMain, ROVER_APP, 50, 16384, 0x0, 0; + CFE_APP, canadarm_app, CanadarmAppMain, CANADARM_APP, 50, 16384, 0x0, 0; CFE_APP, cf, CF_AppMain, CF, 80, 16384, 0x0, 0; CFE_APP, sntp, SNTP_Main, SNTP, 80, 16384, 0x0, 0;
Compile : You can compile cFS in the docker setup using our script:
$ cd ${HOME}/brash_demonstrations $ ./scripts/build_cfe.sh
Test : Your cFS executable should now run the canadarm_app in addition to the rest of pre-existing applications. To test this, try running the
fsw
service:$ cd ${HOME}/brash_demonstrations $ docker compose -f docker-compose-dev.yml up fsw
You’ll see in the terminal some debugging lines showing that the Canadarm app is running:
12.2. Generate ROS2 messages
Now that the app is integrated in our cFS mission, we can generate the ROS2 messages that will let us talk with it using ROS2 from the ground side.
Note
All the steps on this section involve modifying code in the brash workspace, which runs in the rosgsw container, so any instruction regarding the terminal implies the terminal within the rosgsw container. An easy way to do that is to get the rosgsw service running and then have a bash terminal open, e.g.:
$ docker compose -f docker-compose-dev.yml up rosgsw
$ docker exec -it brash_demonstrations-rosgsw-1 bash
Generate the juicer database. You’ll have to edit the launch file to add the canadarm_app .so file to be parsed:
$ cd /code/brash/src $ gedit cfe_ros2_bridge_plugin/juicer_util/launch/generate_juicer_database.launch.py
Update the cfs_path, juicer_path to your specific setup. As for the input_list, add the canadarm_app at the end of the list:
input_list = ['core-cpu1', 'cf/cfe_assert.so', 'cf/ci_lab.so', 'cf/ros_app.so', 'cf/sample_app.so', 'cf/sample_lib.so', 'cf/sbn_f_remap.so', 'cf/sbn.so', 'cf/sbn_udp.so', 'cf/sch_lab.so', 'cf/to_lab.so', 'cf/robot_sim.so', 'cf/cf.so', 'cf/rover_app.so', + 'cf/canadarm_app.so']
Now, generate the database. It might ask you to create the dbs folder in the juicer directory if it is not there already:
$ cd /code/brash $ ros2 launch juicer_util generate_juicer_database.launch.py
Replace the old database with the one you just generated.
$ cd /code/brash/src/cfe_ros2_bridge_plugin/cfe_plugin/resource $ ls # You'll see the cfs_bootes-rc1-dev83_TL.sqlite $ mv /code/juicer/dbs/combined.sqlite cfs_bootes-rc1-dev83_TL.sqlite
and then run the converter. This will create new .msg files for the app within the cfe_msgs folder
$ cd /code/brash $ ros2 launch cfe_msg_converter cfe_msg_converter.launch.py $ colcon build --symlink-install --packages-up-to cfe_msgs
Note that you have to compile the message package after the new .msg files are created.
12.3. Update config file for bridge(s)
Finally! You’ll need to tell the bridges about your new messages, so they can know to hear for them. For the Canadarm example we are using 2 bridges:
cfe_ros2_bridge : Bridge between cfe and the ground
cfe_sbn_bridge : Bridge between cfe and a ROS2 system (the arm) on the flightside.
The Canadarm app has 4 newly generated messages:
CanadarmAppCmdt: Message to send a command from the ground to cfe
CanadarmAppHkTlmt: Message to receive telemetry from cfe to ground.
CanadarmAppRobotStatet: Message that brings the robot state data from the arm to cfe (flight side)
CanadarmAppRobotCommandt: Message that sends the robot command from cfe to the arm (flight side)
The first 2 messages will be added to the configuration file for the ground bridge, and the last 2 messages will be added to the configuration file for the flight bridge:
Ground bridge: Open the
cfe_plugin_config.yaml
file:$ cd /code/brash/src $ cd cfe_ros2_bridge_plugin/juicer_util/config $ gedit cfe_plugin_config.yaml
In the commands and telemetry sections, add references for the first 2 messages:
/**: ros__parameters: plugin_params: juicer_db: ["cfs_bootes-rc1-dev83_TL.sqlite"] map_ros_name: ["src/cfe_ros2_bridge_plugin/cfe_msg_converter/config/map_ros_name.yaml"] commands: ["CPU1CFEESStopAppCmd", "CPU1CFEESRestartAppCmd", ..., + "canadarm_app_cmd"] telemetry: ["CPU1CFEESOneAppTlm", "CPU1CFEESMemStatsTlm", ..., + "canadarm_app_hk_tlm"]
You’ll use these references down the file, in the command_data and telemetry_data section:
command_data: + canadarm_app_cmd: + structure: CanadarmAppCmdt + cfe_mid: '0x1837' + cmd_code: 1 + topic_name: canadarm_app_cmd telemetry_data: + canadarm_app_hk_tlm: + structure: CanadarmAppHkTlmt + cfe_mid: '0x836' + topic_name: canadarm_app_hk_tlm
Each entry has 3 fields:
structure : The name of the ROS2 message generated for this cFE message.
cfe_mid : The identifier for the message, which you must have defined in
canadarm_app/fsw/platform_inc/canadarm_app_msgsids.h
(and which is used insrc/canadarm_app.c
to create the cfe communication channels).topic_name : Name of the ROS2 topic, if you don’t want to use the default.
Note there is one extra entry (cmd_code) for the command message. Do not worry about this one for now, by default, use 1. This value is used for slightly more advanced cFE integration.
Flight bridge: Update the configuration file for the ground bridge. Open the
cfe_plugin_config.yaml
file:$ cd /code/brash/src $ cd cfe_ros2_bridge_plugin/juicer_util/config $ gedit cfe_sbn_plugin_config.yaml
In the commands and telemetry sections, add references for the messages used on the flight side:
/**: ros__parameters: plugin_params: juicer_db: ["cfs_bootes-rc1-dev83_TL.sqlite"] map_ros_name: ["src/cfe_ros2_bridge_plugin/cfe_msg_converter/config/map_ros_name.yaml"] commands: ["CPU1CFEESStopAppCmd", "CPU1CFEESRestartAppCmd", ..., + "canadarm_app_robot_state"] telemetry: ["CPU1CFEESOneAppTlm", "CPU1CFEESMemStatsTlm", ..., + "canadarm_app_robot_command"]
You’ll use these references down the file, in the command_data and telemetry_data section:
command_data: + canadarm_app_robot_state: + structure: CanadarmAppRobotStatet + cfe_mid: '0x1839' + cmd_code: 1 + topic_name: canadarm_app_robot_state telemetry_data: + canadarm_app_robot_command: + structure: CanadarmAppRobotCommandt + cfe_mid: '0x837' + topic_name: canadarm_app_robot_command
You are ready to run!:
Note
You might have noticed that for the flight side bridge, we added a robot state message on the command data, and a robot command message to the telemetry data. This might sound odd at first sight. It helps to think of telemetry as data that goes out of cfe (cfe->) and commands as data that goes into cfe (cfe<-). When you are in the ground side, this goes as expected: Ground user receives telemetry data from cfe (cfe->) and sends commands into cfe (cfe<-).
On the flight side, though, cfe is interacting with a robot, so the command messages for the robot are coming out of cfe (cfe->), which is the telemetry direction. Likewise, the telemetry data from the robot comes into cfe (cfe<-), which is the command direction.
12.4. Test Canadarm demo
Start fsw, rosgsw, rosfsw and novnc:
$ cd ${HOME}/brash_demonstrations $ docker compose -f docker-compose-dev.yml up
Open a terminal and point it to: http://localhost:8080/vnc.html to see the GUI/windows.
Open a terminal in the rosfsw container and start the canadarm demo and the node that will move the arm upon command:
$ docker exec -it brash_demonstrations-rosfsw-1 bash $ ros2 launch brash_application_tools flight_canadarm.launch.py
Open a terminal in the rosgsw (ground) container.
$ docker exec -it brash_demonstrations-rosgsw-1 bash
Now, try moving the arm to an open pose (mode=0):
$ ros2 run brash_application_tools canadarm_send_command --ros-args -p mode:=0
The arm will move. After it is done, you can try moving to mode 1 and 2.