<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://rsewiki.electro.dtu.dk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=S253809</id>
	<title>Rsewiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://rsewiki.electro.dtu.dk/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=S253809"/>
	<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Special:Contributions/S253809"/>
	<updated>2026-06-10T13:17:23Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.1</generator>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8965</id>
		<title>Fejemis 2026</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8965"/>
		<updated>2026-05-28T09:17:40Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Mission Statement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Fejemis is an autonomous cleaning robot developed to clean the Asta building. The building is divided into different cleaning areas, where the robot navigates autonomously to a selected point, plans a cleaning path, and performs the cleaning task using its brushing system while following the generated route.&lt;br /&gt;
&lt;br /&gt;
[[File:Fejemis.jpeg | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Start up guide ==&lt;br /&gt;
&lt;br /&gt;
To turn on Fejemis, you need to press the turn on button for more than 2 seconds. If not, both Raspberry Pis wont boot. To turn it off you need to press it for more than 7 seconds.&lt;br /&gt;
&lt;br /&gt;
To work with Fejemis, you can connect directly using a monitor and keyboard. The monitor can be connected to one of the Raspberry Pis, while the keyboard connects through USB. After booting, log into the Ubuntu system using the provided credentials.&lt;br /&gt;
&lt;br /&gt;
Fejemis uses two Raspberry Pis:&lt;br /&gt;
&lt;br /&gt;
Main Raspberry Pi and Auxiliary Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pis can be also accessed through SSH.(The IP some time changes, check it by connecting directly with the monitor)&lt;br /&gt;
 Main lab&lt;br /&gt;
 local@10.197.213.227&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.213.226&lt;br /&gt;
 Main asta&lt;br /&gt;
 local@10.197.219.238&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.219.237&lt;br /&gt;
&lt;br /&gt;
The last way of connecting is through Ethernet. &lt;br /&gt;
 Main&lt;br /&gt;
 197.168.1.1 &lt;br /&gt;
 Aux&lt;br /&gt;
 197.168.1.2&lt;br /&gt;
&lt;br /&gt;
If you are connected to the main Raspberry Pi,the auxiliary Raspberry Pi can also be accessed using:&lt;br /&gt;
 &lt;br /&gt;
 ssh fejemis-aux&lt;br /&gt;
&lt;br /&gt;
=== Launch===&lt;br /&gt;
&lt;br /&gt;
Launching the bridge (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge bridge.launch.py&lt;br /&gt;
&lt;br /&gt;
Launching the mixer + gamepad control (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge mixer.launch.py&lt;br /&gt;
&lt;br /&gt;
You need to press the &amp;quot;BACK&amp;quot; button on the gamepad to actually start the control from the gamepad.&lt;br /&gt;
&lt;br /&gt;
You can launch both at once with the following command: ros2 launch fejemis_bridge control.launch.py&lt;br /&gt;
&lt;br /&gt;
This launch file runs the latest version of the autonomous cleaning software:&lt;br /&gt;
&lt;br /&gt;
 ros2 launch fejemis robot.launch.py &lt;br /&gt;
&lt;br /&gt;
It must be launched on both Raspberry Pis, since each device handles different parts of the system.&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
 localization:=true/false, map_location:=asta/lab&lt;br /&gt;
&lt;br /&gt;
Default: localization true, map loc asta&lt;br /&gt;
&lt;br /&gt;
If the argument &amp;quot;localization:=false&amp;quot; is provided, the robot will automatically start a new mapping process and overwrite the current map.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After &#039;&#039;fejemis robot.launch.py&#039;&#039; is running on both Raspberry Pis, you can optionally start a few helper nodes depending on what you want to test:&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc rviz_goal_relay.py&amp;lt;/code&amp;gt; lets you send a navigation goal from RViz if you want to drive the robot to a specific point.&lt;br /&gt;
* If the robot does not localize itself automatically, you can set the initial pose in RViz.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc mission_trigger.py&amp;lt;/code&amp;gt; starts the cleaning behaviour once the robot is ready.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc fixed_complete_coverage_planner.py&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ros2 run fejemis_maploc complete_coverage_planner.py&amp;lt;/code&amp;gt; can be used to test only the cleaning path generation and coverage following.&lt;br /&gt;
&lt;br /&gt;
If Fejemis crash (one of the battery display will turn off), you need to plug it in before turning it on again.&lt;br /&gt;
&lt;br /&gt;
=== Troubleshooting / usual problems ===&lt;br /&gt;
&lt;br /&gt;
Common issues seen during daily operation:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Main Raspberry Pi crashes while building the full workspace&#039;&#039;&#039;&lt;br /&gt;
The main Raspberry Pi may crash or run out of resources when building everything at once.&lt;br /&gt;
Use symlink install and only build the packages you changed:&lt;br /&gt;
&lt;br /&gt;
  colcon build --symlink-install --packages-select &amp;lt;changed_package_1&amp;gt; &amp;lt;changed_package_2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Robot shuts down while driving (high speed / acceleration)&#039;&#039;&#039;&lt;br /&gt;
If commanded speed or acceleration is too aggressive, the battery management system can cut power and both Raspberry Pis may shut down.&lt;br /&gt;
Reduce drive aggressiveness and avoid sudden acceleration spikes.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;System behaves inconsistently between main and aux Pi&#039;&#039;&#039;&lt;br /&gt;
This is often caused by the two Raspberry Pis having unsynchronised clocks.&lt;br /&gt;
Run this from the main Raspberry Pi:&lt;br /&gt;
&lt;br /&gt;
  fejemis sync_clocks&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Only one Raspberry Pi appears to boot&#039;&#039;&#039;&lt;br /&gt;
You must hold the power button for a few seconds so both Raspberry Pis start correctly.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Left battery does not engage after BMS cutoff&#039;&#039;&#039;&lt;br /&gt;
If the left battery turns off due to battery management protection, plug in the robot before booting again, otherwise that battery may not re-engage.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Unstable wireless connection in ASTA&#039;&#039;&#039;&lt;br /&gt;
Wi-Fi in ASTA is often unreliable. Prefer connecting your PC over Ethernet for stable control and monitoring.&lt;br /&gt;
&lt;br /&gt;
=== Other utilities ===&lt;br /&gt;
&lt;br /&gt;
If you want to generate a square signal, you can use the node I made:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator --ros-args -p frequency:=&amp;lt;signal_freq&amp;gt; -p out_type:=&amp;lt;output ROS2 type&amp;gt; -r /sig_out:=&amp;lt;output topic&amp;gt; -p bias:=&amp;lt;signal bias&amp;gt; -p range:=&amp;lt;vmax-vmin&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typically you wight want to send it as if it was a gamepad input:&lt;br /&gt;
&lt;br /&gt;
 ros2 run arcana_tools signal_generator--ros-args -p frequency:=0.5 -p out_type:=geometry_msgs.msg.Twist.linear.x -r /sig_out:=/joy_control/cmd_vel -p range:=0.5&lt;br /&gt;
&lt;br /&gt;
You can display graphs using the rqt tool (type rqt in a terminal). There&#039;s a plugin &amp;quot;Vizualization&amp;gt;Plot&amp;quot; that integrate the python 3 matplotlib package.&lt;br /&gt;
&lt;br /&gt;
To enable the fetching of the wheels velocities from the bridge, you may need to edit the file &amp;quot;~/.config/fejemis_bridge/bridge.yaml&amp;quot; and set enable: true instead of false in the vel section.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Mechanics ===&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Mechanics]] in this page. &lt;br /&gt;
&lt;br /&gt;
=== Electronics ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Electronics]] in this page.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
The software is split into two components:&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis bridge]]&#039;&#039;&#039; — handles communication between the main Raspberry Pi and the Teensy controllers and provides a manual/remote-control interface. See [[Fejemis repository]] for source and docs.&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis ROS2 Software]]&#039;&#039;&#039; — higher-level programs for mapping, navigation, and robot behaviours located in &#039;&#039;&#039;Fejemis_workspace_2026&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Old repository ====&lt;br /&gt;
You can access the description of the legacy  through this link.&lt;br /&gt;
&lt;br /&gt;
== Mission Statement ==&lt;br /&gt;
Fejemis is developed to autonomously sweep the ASTA building in a safe and practical way for daily operations.&lt;br /&gt;
&lt;br /&gt;
The robot should:&lt;br /&gt;
* Navigate autonomously through the building and reach assigned cleaning areas.&lt;br /&gt;
* Plan and execute a full cleaning strategy instead of only following a single manual path.&lt;br /&gt;
* Interrupt its cleaning mission when needed to return to a docking station for charging, then resume operation.&lt;br /&gt;
* Avoid interfering with ongoing work in the ASTA building by operating safely around people and active tasks.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8964</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8964"/>
		<updated>2026-05-28T09:07:45Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Convenience commands */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
In this page we focus on the ROS 2 software stack to learn more about the low-level microcontroller integration see [[Fejemis bridge]] and [[Fejemis repository]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[Fejemis Description ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo. For more detailed information see [[Fejemis Sim ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Dependencies (src/deps) ==&lt;br /&gt;
&lt;br /&gt;
The repository vendors a set of third-party and support packages under ``src/deps``. These are kept in-tree to ensure reproducible builds and known-good versions for Fejemis.&lt;br /&gt;
&lt;br /&gt;
For a short index and per-package summaries see [[Dependencies Index]].&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
  git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
  cd Fejemis_Workspace_2026&lt;br /&gt;
  ./install.sh&lt;br /&gt;
  source ./activate.sh&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
 fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
 fejemis sync_clocks&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8963</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8963"/>
		<updated>2026-05-28T09:07:24Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Multi pi setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
In this page we focus on the ROS 2 software stack to learn more about the low-level microcontroller integration see [[Fejemis bridge]] and [[Fejemis repository]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[Fejemis Description ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo. For more detailed information see [[Fejemis Sim ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Dependencies (src/deps) ==&lt;br /&gt;
&lt;br /&gt;
The repository vendors a set of third-party and support packages under ``src/deps``. These are kept in-tree to ensure reproducible builds and known-good versions for Fejemis.&lt;br /&gt;
&lt;br /&gt;
For a short index and per-package summaries see [[Dependencies Index]].&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
  git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
  cd Fejemis_Workspace_2026&lt;br /&gt;
  ./install.sh&lt;br /&gt;
  source ./activate.sh&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Description_ROS2&amp;diff=8962</id>
		<title>Fejemis Description ROS2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Description_ROS2&amp;diff=8962"/>
		<updated>2026-05-28T09:04:54Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Typical Usage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_description&#039;&#039; is the robot description package for Fejemis. It defines the robot model using URDF/Xacro and publishes the TF model state so the rest of the ROS 2 stack can use a consistent robot geometry.&lt;br /&gt;
&lt;br /&gt;
The package is responsible for:&lt;br /&gt;
&lt;br /&gt;
* Building the Fejemis robot model from modular xacro files.&lt;br /&gt;
* Publishing &#039;&#039;robot_description&#039;&#039; and TF through &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
* Providing a simulation-friendly wheel configuration through launch arguments.&lt;br /&gt;
* Keeping a shared frame and link structure used by simulation, mapping, and navigation.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;launch&#039;&#039; - launch files for model generation and state publishing.&lt;br /&gt;
* &#039;&#039;xacro&#039;&#039; - modular robot model definition files.&lt;br /&gt;
* &#039;&#039;config&#039;&#039; - controller/simulation-related configuration.&lt;br /&gt;
&lt;br /&gt;
Main xacro files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;xacro/robot.urdf.xacro&#039;&#039; - top-level robot assembly file.&lt;br /&gt;
* &#039;&#039;xacro/robot_core.xacro&#039;&#039; - chassis and base links.&lt;br /&gt;
* &#039;&#039;xacro/robot_wheels.xacro&#039;&#039; - drive wheels and Gazebo diff-drive plugin.&lt;br /&gt;
* &#039;&#039;xacro/robot_castor.xacro&#039;&#039; - castor wheel definition.&lt;br /&gt;
* &#039;&#039;xacro/sensor_imu.xacro&#039;&#039; - IMU and magnetometer links/sensors.&lt;br /&gt;
* &#039;&#039;xacro/sensor_lidar.xacro&#039;&#039; - LiDAR link and simulated scan sensor.&lt;br /&gt;
* &#039;&#039;xacro/sensor_depth_camera.xacro&#039;&#039; - depth camera and optical frame.&lt;br /&gt;
* &#039;&#039;xacro/utils_inertial.xacro&#039;&#039; and &#039;&#039;xacro/utils_colors.xacro&#039;&#039; - reusable inertial and visual macros.&lt;br /&gt;
&lt;br /&gt;
== Launch File ==&lt;br /&gt;
&lt;br /&gt;
=== launch/model.launch.py ===&lt;br /&gt;
&lt;br /&gt;
This launch file generates the URDF from xacro and starts &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulation clock when &#039;&#039;true&#039;&#039;. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Behavior:&lt;br /&gt;
&lt;br /&gt;
* Processes &#039;&#039;fejemis_description::xacro/robot.urdf.xacro&#039;&#039; with xacro.&lt;br /&gt;
* Passes &#039;&#039;fixed_wheels:=&amp;lt;use_sim_time&amp;gt;&#039;&#039; into xacro.&lt;br /&gt;
* Publishes &#039;&#039;robot_description&#039;&#039; parameter to &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
* Publishes the model TF tree for downstream packages.&lt;br /&gt;
&lt;br /&gt;
== Model Composition ==&lt;br /&gt;
&lt;br /&gt;
The top-level model in &#039;&#039;robot.urdf.xacro&#039;&#039; assembles the robot in this order:&lt;br /&gt;
&lt;br /&gt;
* Core chassis: &#039;&#039;xacro:core&#039;&#039;&lt;br /&gt;
* Drive wheels: &#039;&#039;xacro:wheels&#039;&#039;&lt;br /&gt;
* Castor wheel: &#039;&#039;xacro:castor&#039;&#039;&lt;br /&gt;
* IMU and magnetometer: &#039;&#039;xacro:imu_sensor&#039;&#039;&lt;br /&gt;
* LiDAR: &#039;&#039;xacro:lidar&#039;&#039;&lt;br /&gt;
* Depth camera: &#039;&#039;xacro:depth&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There is also an optional camera macro (currently commented in the top-level xacro).&lt;br /&gt;
&lt;br /&gt;
== Main Links and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common links/frames provided by the description:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - primary base frame for navigation and odometry integration.&lt;br /&gt;
* &#039;&#039;chassis&#039;&#039; - main physical body link.&lt;br /&gt;
* &#039;&#039;left_wheel&#039;&#039; and &#039;&#039;right_wheel&#039;&#039; - drive wheel links.&lt;br /&gt;
* &#039;&#039;laser_frame&#039;&#039; - LiDAR mounting and scan frame in simulation.&lt;br /&gt;
* &#039;&#039;imu_link&#039;&#039; - IMU and magnetometer frame.&lt;br /&gt;
* &#039;&#039;depth_link&#039;&#039; and &#039;&#039;depth_link_optical&#039;&#039; - depth camera body and optical frame.&lt;br /&gt;
&lt;br /&gt;
These frames are consumed by mapping/localisation and simulation bridge layers.&lt;br /&gt;
&lt;br /&gt;
== Simulation-Specific Behavior ==&lt;br /&gt;
&lt;br /&gt;
The wheel macro supports both fixed and rotating wheel joints:&lt;br /&gt;
&lt;br /&gt;
* Real robot path: wheel joints are fixed in the model launch context.&lt;br /&gt;
* Simulation path: wheels are set to continuous joints when simulation mode is enabled.&lt;br /&gt;
&lt;br /&gt;
Gazebo systems configured in &#039;&#039;robot_wheels.xacro&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;gz::sim::systems::DiffDrive&#039;&#039;&lt;br /&gt;
* &#039;&#039;gz::sim::systems::JointStatePublisher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diff-drive plugin settings include:&lt;br /&gt;
&lt;br /&gt;
* Wheel separation: &#039;&#039;0.3847&#039;&#039;&lt;br /&gt;
* Wheel diameter: &#039;&#039;0.2&#039;&#039;&lt;br /&gt;
* Command topic: &#039;&#039;/model/fejemis/cmd_vel&#039;&#039;&lt;br /&gt;
* Odometry topic: &#039;&#039;/model/fejemis/odom&#039;&#039;&lt;br /&gt;
* Frame IDs: &#039;&#039;odom&#039;&#039; and &#039;&#039;base_link&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Drive wheel friction is increased in simulation to reduce slip, while castor behavior is defined separately in the castor xacro.&lt;br /&gt;
&lt;br /&gt;
== Sensor Simulation Topics ==&lt;br /&gt;
&lt;br /&gt;
The description also defines simulated sensor outputs used by the bridge layer:&lt;br /&gt;
&lt;br /&gt;
* LiDAR: &#039;&#039;/model/fejemis/scan&#039;&#039;&lt;br /&gt;
* IMU: &#039;&#039;/model/fejemis/imu&#039;&#039;&lt;br /&gt;
* Magnetometer: &#039;&#039;/model/fejemis/mag&#039;&#039;&lt;br /&gt;
* Depth camera: &#039;&#039;/model/fejemis/depth_camera&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These topics are later bridged into ROS topics by &#039;&#039;fejemis_sim&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start robot description publisher:&lt;br /&gt;
  ros2 launch fejemis_description model.launch.py&lt;br /&gt;
&lt;br /&gt;
Start with simulation time:&lt;br /&gt;
  ros2 launch fejemis_description model.launch.py use_sim_time:=true&lt;br /&gt;
&lt;br /&gt;
In most full-system runs, this launch file is included by top-level bringup (for example in simulation launch stacks).&lt;br /&gt;
&lt;br /&gt;
== Relationship To Other Packages ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis_sim&#039;&#039; uses this package to spawn the robot from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; and Nav2 depend on the frame structure defined here.&lt;br /&gt;
* Sensor and odometry pipelines assume the link names and frame conventions from this model.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Description_ROS2&amp;diff=8960</id>
		<title>Fejemis Description ROS2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Description_ROS2&amp;diff=8960"/>
		<updated>2026-05-28T09:03:59Z</updated>

		<summary type="html">&lt;p&gt;S253809: Created page with &amp;quot;Back to Fejemis ROS2 Software  == Purpose ==  &amp;#039;&amp;#039;fejemis_description&amp;#039;&amp;#039; is the robot description package for Fejemis. It defines the robot model using URDF/Xacro and publishes the TF model state so the rest of the ROS 2 stack can use a consistent robot geometry.  The package is responsible for:  * Building the Fejemis robot model from modular xacro files. * Publishing &amp;#039;&amp;#039;robot_description&amp;#039;&amp;#039; and TF through &amp;#039;&amp;#039;robot_state_publisher&amp;#039;&amp;#039;. * Providing a simulation-friendly whee...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_description&#039;&#039; is the robot description package for Fejemis. It defines the robot model using URDF/Xacro and publishes the TF model state so the rest of the ROS 2 stack can use a consistent robot geometry.&lt;br /&gt;
&lt;br /&gt;
The package is responsible for:&lt;br /&gt;
&lt;br /&gt;
* Building the Fejemis robot model from modular xacro files.&lt;br /&gt;
* Publishing &#039;&#039;robot_description&#039;&#039; and TF through &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
* Providing a simulation-friendly wheel configuration through launch arguments.&lt;br /&gt;
* Keeping a shared frame and link structure used by simulation, mapping, and navigation.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;launch&#039;&#039; - launch files for model generation and state publishing.&lt;br /&gt;
* &#039;&#039;xacro&#039;&#039; - modular robot model definition files.&lt;br /&gt;
* &#039;&#039;config&#039;&#039; - controller/simulation-related configuration.&lt;br /&gt;
&lt;br /&gt;
Main xacro files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;xacro/robot.urdf.xacro&#039;&#039; - top-level robot assembly file.&lt;br /&gt;
* &#039;&#039;xacro/robot_core.xacro&#039;&#039; - chassis and base links.&lt;br /&gt;
* &#039;&#039;xacro/robot_wheels.xacro&#039;&#039; - drive wheels and Gazebo diff-drive plugin.&lt;br /&gt;
* &#039;&#039;xacro/robot_castor.xacro&#039;&#039; - castor wheel definition.&lt;br /&gt;
* &#039;&#039;xacro/sensor_imu.xacro&#039;&#039; - IMU and magnetometer links/sensors.&lt;br /&gt;
* &#039;&#039;xacro/sensor_lidar.xacro&#039;&#039; - LiDAR link and simulated scan sensor.&lt;br /&gt;
* &#039;&#039;xacro/sensor_depth_camera.xacro&#039;&#039; - depth camera and optical frame.&lt;br /&gt;
* &#039;&#039;xacro/utils_inertial.xacro&#039;&#039; and &#039;&#039;xacro/utils_colors.xacro&#039;&#039; - reusable inertial and visual macros.&lt;br /&gt;
&lt;br /&gt;
== Launch File ==&lt;br /&gt;
&lt;br /&gt;
=== launch/model.launch.py ===&lt;br /&gt;
&lt;br /&gt;
This launch file generates the URDF from xacro and starts &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulation clock when &#039;&#039;true&#039;&#039;. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Behavior:&lt;br /&gt;
&lt;br /&gt;
* Processes &#039;&#039;fejemis_description::xacro/robot.urdf.xacro&#039;&#039; with xacro.&lt;br /&gt;
* Passes &#039;&#039;fixed_wheels:=&amp;lt;use_sim_time&amp;gt;&#039;&#039; into xacro.&lt;br /&gt;
* Publishes &#039;&#039;robot_description&#039;&#039; parameter to &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
* Publishes the model TF tree for downstream packages.&lt;br /&gt;
&lt;br /&gt;
== Model Composition ==&lt;br /&gt;
&lt;br /&gt;
The top-level model in &#039;&#039;robot.urdf.xacro&#039;&#039; assembles the robot in this order:&lt;br /&gt;
&lt;br /&gt;
* Core chassis: &#039;&#039;xacro:core&#039;&#039;&lt;br /&gt;
* Drive wheels: &#039;&#039;xacro:wheels&#039;&#039;&lt;br /&gt;
* Castor wheel: &#039;&#039;xacro:castor&#039;&#039;&lt;br /&gt;
* IMU and magnetometer: &#039;&#039;xacro:imu_sensor&#039;&#039;&lt;br /&gt;
* LiDAR: &#039;&#039;xacro:lidar&#039;&#039;&lt;br /&gt;
* Depth camera: &#039;&#039;xacro:depth&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There is also an optional camera macro (currently commented in the top-level xacro).&lt;br /&gt;
&lt;br /&gt;
== Main Links and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common links/frames provided by the description:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - primary base frame for navigation and odometry integration.&lt;br /&gt;
* &#039;&#039;chassis&#039;&#039; - main physical body link.&lt;br /&gt;
* &#039;&#039;left_wheel&#039;&#039; and &#039;&#039;right_wheel&#039;&#039; - drive wheel links.&lt;br /&gt;
* &#039;&#039;laser_frame&#039;&#039; - LiDAR mounting and scan frame in simulation.&lt;br /&gt;
* &#039;&#039;imu_link&#039;&#039; - IMU and magnetometer frame.&lt;br /&gt;
* &#039;&#039;depth_link&#039;&#039; and &#039;&#039;depth_link_optical&#039;&#039; - depth camera body and optical frame.&lt;br /&gt;
&lt;br /&gt;
These frames are consumed by mapping/localisation and simulation bridge layers.&lt;br /&gt;
&lt;br /&gt;
== Simulation-Specific Behavior ==&lt;br /&gt;
&lt;br /&gt;
The wheel macro supports both fixed and rotating wheel joints:&lt;br /&gt;
&lt;br /&gt;
* Real robot path: wheel joints are fixed in the model launch context.&lt;br /&gt;
* Simulation path: wheels are set to continuous joints when simulation mode is enabled.&lt;br /&gt;
&lt;br /&gt;
Gazebo systems configured in &#039;&#039;robot_wheels.xacro&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;gz::sim::systems::DiffDrive&#039;&#039;&lt;br /&gt;
* &#039;&#039;gz::sim::systems::JointStatePublisher&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diff-drive plugin settings include:&lt;br /&gt;
&lt;br /&gt;
* Wheel separation: &#039;&#039;0.3847&#039;&#039;&lt;br /&gt;
* Wheel diameter: &#039;&#039;0.2&#039;&#039;&lt;br /&gt;
* Command topic: &#039;&#039;/model/fejemis/cmd_vel&#039;&#039;&lt;br /&gt;
* Odometry topic: &#039;&#039;/model/fejemis/odom&#039;&#039;&lt;br /&gt;
* Frame IDs: &#039;&#039;odom&#039;&#039; and &#039;&#039;base_link&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Drive wheel friction is increased in simulation to reduce slip, while castor behavior is defined separately in the castor xacro.&lt;br /&gt;
&lt;br /&gt;
== Sensor Simulation Topics ==&lt;br /&gt;
&lt;br /&gt;
The description also defines simulated sensor outputs used by the bridge layer:&lt;br /&gt;
&lt;br /&gt;
* LiDAR: &#039;&#039;/model/fejemis/scan&#039;&#039;&lt;br /&gt;
* IMU: &#039;&#039;/model/fejemis/imu&#039;&#039;&lt;br /&gt;
* Magnetometer: &#039;&#039;/model/fejemis/mag&#039;&#039;&lt;br /&gt;
* Depth camera: &#039;&#039;/model/fejemis/depth_camera&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These topics are later bridged into ROS topics by &#039;&#039;fejemis_sim&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start robot description publisher:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 launch fejemis_description model.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Start with simulation time:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 launch fejemis_description model.launch.py use_sim_time:=true&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
In most full-system runs, this launch file is included by top-level bringup (for example in simulation launch stacks).&lt;br /&gt;
&lt;br /&gt;
== Relationship To Other Packages ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis_sim&#039;&#039; uses this package to spawn the robot from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; and Nav2 depend on the frame structure defined here.&lt;br /&gt;
* Sensor and odometry pipelines assume the link names and frame conventions from this model.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8959</id>
		<title>Fejemis 2026</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8959"/>
		<updated>2026-05-28T08:56:51Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Troubleshooting / usual problems */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Fejemis is an autonomous cleaning robot developed to clean the Asta building. The building is divided into different cleaning areas, where the robot navigates autonomously to a selected point, plans a cleaning path, and performs the cleaning task using its brushing system while following the generated route.&lt;br /&gt;
&lt;br /&gt;
[[File:Fejemis.jpeg | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Start up guide ==&lt;br /&gt;
&lt;br /&gt;
To turn on Fejemis, you need to press the turn on button for more than 2 seconds. If not, both Raspberry Pis wont boot. To turn it off you need to press it for more than 7 seconds.&lt;br /&gt;
&lt;br /&gt;
To work with Fejemis, you can connect directly using a monitor and keyboard. The monitor can be connected to one of the Raspberry Pis, while the keyboard connects through USB. After booting, log into the Ubuntu system using the provided credentials.&lt;br /&gt;
&lt;br /&gt;
Fejemis uses two Raspberry Pis:&lt;br /&gt;
&lt;br /&gt;
Main Raspberry Pi and Auxiliary Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pis can be also accessed through SSH.(The IP some time changes, check it by connecting directly with the monitor)&lt;br /&gt;
 Main lab&lt;br /&gt;
 local@10.197.213.227&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.213.226&lt;br /&gt;
 Main asta&lt;br /&gt;
 local@10.197.219.238&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.219.237&lt;br /&gt;
&lt;br /&gt;
The last way of connecting is through Ethernet. &lt;br /&gt;
 Main&lt;br /&gt;
 197.168.1.1 &lt;br /&gt;
 Aux&lt;br /&gt;
 197.168.1.2&lt;br /&gt;
&lt;br /&gt;
If you are connected to the main Raspberry Pi,the auxiliary Raspberry Pi can also be accessed using:&lt;br /&gt;
 &lt;br /&gt;
 ssh fejemis-aux&lt;br /&gt;
&lt;br /&gt;
=== Launch===&lt;br /&gt;
&lt;br /&gt;
Launching the bridge (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge bridge.launch.py&lt;br /&gt;
&lt;br /&gt;
Launching the mixer + gamepad control (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge mixer.launch.py&lt;br /&gt;
&lt;br /&gt;
You need to press the &amp;quot;BACK&amp;quot; button on the gamepad to actually start the control from the gamepad.&lt;br /&gt;
&lt;br /&gt;
You can launch both at once with the following command: ros2 launch fejemis_bridge control.launch.py&lt;br /&gt;
&lt;br /&gt;
This launch file runs the latest version of the autonomous cleaning software:&lt;br /&gt;
&lt;br /&gt;
 ros2 launch fejemis robot.launch.py &lt;br /&gt;
&lt;br /&gt;
It must be launched on both Raspberry Pis, since each device handles different parts of the system.&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
 localization:=true/false, map_location:=asta/lab&lt;br /&gt;
&lt;br /&gt;
Default: localization true, map loc asta&lt;br /&gt;
&lt;br /&gt;
If the argument &amp;quot;localization:=false&amp;quot; is provided, the robot will automatically start a new mapping process and overwrite the current map.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After &#039;&#039;fejemis robot.launch.py&#039;&#039; is running on both Raspberry Pis, you can optionally start a few helper nodes depending on what you want to test:&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc rviz_goal_relay.py&amp;lt;/code&amp;gt; lets you send a navigation goal from RViz if you want to drive the robot to a specific point.&lt;br /&gt;
* If the robot does not localize itself automatically, you can set the initial pose in RViz.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc mission_trigger.py&amp;lt;/code&amp;gt; starts the cleaning behaviour once the robot is ready.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc fixed_complete_coverage_planner.py&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ros2 run fejemis_maploc complete_coverage_planner.py&amp;lt;/code&amp;gt; can be used to test only the cleaning path generation and coverage following.&lt;br /&gt;
&lt;br /&gt;
If Fejemis crash (one of the battery display will turn off), you need to plug it in before turning it on again.&lt;br /&gt;
&lt;br /&gt;
=== Troubleshooting / usual problems ===&lt;br /&gt;
&lt;br /&gt;
Common issues seen during daily operation:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Main Raspberry Pi crashes while building the full workspace&#039;&#039;&#039;&lt;br /&gt;
The main Raspberry Pi may crash or run out of resources when building everything at once.&lt;br /&gt;
Use symlink install and only build the packages you changed:&lt;br /&gt;
&lt;br /&gt;
  colcon build --symlink-install --packages-select &amp;lt;changed_package_1&amp;gt; &amp;lt;changed_package_2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Robot shuts down while driving (high speed / acceleration)&#039;&#039;&#039;&lt;br /&gt;
If commanded speed or acceleration is too aggressive, the battery management system can cut power and both Raspberry Pis may shut down.&lt;br /&gt;
Reduce drive aggressiveness and avoid sudden acceleration spikes.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;System behaves inconsistently between main and aux Pi&#039;&#039;&#039;&lt;br /&gt;
This is often caused by the two Raspberry Pis having unsynchronised clocks.&lt;br /&gt;
Run this from the main Raspberry Pi:&lt;br /&gt;
&lt;br /&gt;
  fejemis sync_clocks&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Only one Raspberry Pi appears to boot&#039;&#039;&#039;&lt;br /&gt;
You must hold the power button for a few seconds so both Raspberry Pis start correctly.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Left battery does not engage after BMS cutoff&#039;&#039;&#039;&lt;br /&gt;
If the left battery turns off due to battery management protection, plug in the robot before booting again, otherwise that battery may not re-engage.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Unstable wireless connection in ASTA&#039;&#039;&#039;&lt;br /&gt;
Wi-Fi in ASTA is often unreliable. Prefer connecting your PC over Ethernet for stable control and monitoring.&lt;br /&gt;
&lt;br /&gt;
=== Other utilities ===&lt;br /&gt;
&lt;br /&gt;
If you want to generate a square signal, you can use the node I made:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator --ros-args -p frequency:=&amp;lt;signal_freq&amp;gt; -p out_type:=&amp;lt;output ROS2 type&amp;gt; -r /sig_out:=&amp;lt;output topic&amp;gt; -p bias:=&amp;lt;signal bias&amp;gt; -p range:=&amp;lt;vmax-vmin&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typically you wight want to send it as if it was a gamepad input:&lt;br /&gt;
&lt;br /&gt;
 ros2 run arcana_tools signal_generator--ros-args -p frequency:=0.5 -p out_type:=geometry_msgs.msg.Twist.linear.x -r /sig_out:=/joy_control/cmd_vel -p range:=0.5&lt;br /&gt;
&lt;br /&gt;
You can display graphs using the rqt tool (type rqt in a terminal). There&#039;s a plugin &amp;quot;Vizualization&amp;gt;Plot&amp;quot; that integrate the python 3 matplotlib package.&lt;br /&gt;
&lt;br /&gt;
To enable the fetching of the wheels velocities from the bridge, you may need to edit the file &amp;quot;~/.config/fejemis_bridge/bridge.yaml&amp;quot; and set enable: true instead of false in the vel section.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Mechanics ===&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Mechanics]] in this page. &lt;br /&gt;
&lt;br /&gt;
=== Electronics ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Electronics]] in this page.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
The software is split into two components:&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis bridge]]&#039;&#039;&#039; — handles communication between the main Raspberry Pi and the Teensy controllers and provides a manual/remote-control interface. See [[Fejemis repository]] for source and docs.&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis ROS2 Software]]&#039;&#039;&#039; — higher-level programs for mapping, navigation, and robot behaviours located in &#039;&#039;&#039;Fejemis_workspace_2026&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Old repository ====&lt;br /&gt;
You can access the description of the legacy  through this link.&lt;br /&gt;
&lt;br /&gt;
== Mission Statement ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8958</id>
		<title>Fejemis 2026</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8958"/>
		<updated>2026-05-28T08:54:09Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Fejemis is an autonomous cleaning robot developed to clean the Asta building. The building is divided into different cleaning areas, where the robot navigates autonomously to a selected point, plans a cleaning path, and performs the cleaning task using its brushing system while following the generated route.&lt;br /&gt;
&lt;br /&gt;
[[File:Fejemis.jpeg | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Start up guide ==&lt;br /&gt;
&lt;br /&gt;
To turn on Fejemis, you need to press the turn on button for more than 2 seconds. If not, both Raspberry Pis wont boot. To turn it off you need to press it for more than 7 seconds.&lt;br /&gt;
&lt;br /&gt;
To work with Fejemis, you can connect directly using a monitor and keyboard. The monitor can be connected to one of the Raspberry Pis, while the keyboard connects through USB. After booting, log into the Ubuntu system using the provided credentials.&lt;br /&gt;
&lt;br /&gt;
Fejemis uses two Raspberry Pis:&lt;br /&gt;
&lt;br /&gt;
Main Raspberry Pi and Auxiliary Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pis can be also accessed through SSH.(The IP some time changes, check it by connecting directly with the monitor)&lt;br /&gt;
 Main lab&lt;br /&gt;
 local@10.197.213.227&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.213.226&lt;br /&gt;
 Main asta&lt;br /&gt;
 local@10.197.219.238&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.219.237&lt;br /&gt;
&lt;br /&gt;
The last way of connecting is through Ethernet. &lt;br /&gt;
 Main&lt;br /&gt;
 197.168.1.1 &lt;br /&gt;
 Aux&lt;br /&gt;
 197.168.1.2&lt;br /&gt;
&lt;br /&gt;
If you are connected to the main Raspberry Pi,the auxiliary Raspberry Pi can also be accessed using:&lt;br /&gt;
 &lt;br /&gt;
 ssh fejemis-aux&lt;br /&gt;
&lt;br /&gt;
=== Launch===&lt;br /&gt;
&lt;br /&gt;
Launching the bridge (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge bridge.launch.py&lt;br /&gt;
&lt;br /&gt;
Launching the mixer + gamepad control (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge mixer.launch.py&lt;br /&gt;
&lt;br /&gt;
You need to press the &amp;quot;BACK&amp;quot; button on the gamepad to actually start the control from the gamepad.&lt;br /&gt;
&lt;br /&gt;
You can launch both at once with the following command: ros2 launch fejemis_bridge control.launch.py&lt;br /&gt;
&lt;br /&gt;
This launch file runs the latest version of the autonomous cleaning software:&lt;br /&gt;
&lt;br /&gt;
 ros2 launch fejemis robot.launch.py &lt;br /&gt;
&lt;br /&gt;
It must be launched on both Raspberry Pis, since each device handles different parts of the system.&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
 localization:=true/false, map_location:=asta/lab&lt;br /&gt;
&lt;br /&gt;
Default: localization true, map loc asta&lt;br /&gt;
&lt;br /&gt;
If the argument &amp;quot;localization:=false&amp;quot; is provided, the robot will automatically start a new mapping process and overwrite the current map.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After &#039;&#039;fejemis robot.launch.py&#039;&#039; is running on both Raspberry Pis, you can optionally start a few helper nodes depending on what you want to test:&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc rviz_goal_relay.py&amp;lt;/code&amp;gt; lets you send a navigation goal from RViz if you want to drive the robot to a specific point.&lt;br /&gt;
* If the robot does not localize itself automatically, you can set the initial pose in RViz.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc mission_trigger.py&amp;lt;/code&amp;gt; starts the cleaning behaviour once the robot is ready.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc fixed_complete_coverage_planner.py&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ros2 run fejemis_maploc complete_coverage_planner.py&amp;lt;/code&amp;gt; can be used to test only the cleaning path generation and coverage following.&lt;br /&gt;
&lt;br /&gt;
If Fejemis crash (one of the battery display will turn off), you need to plug it in before turning it on again.&lt;br /&gt;
&lt;br /&gt;
=== Troubleshooting / usual problems ===&lt;br /&gt;
&lt;br /&gt;
Common issues seen during daily operation:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Main Raspberry Pi crashes while building the full workspace&#039;&#039;&#039;&lt;br /&gt;
The main Raspberry Pi may crash or run out of resources when building everything at once.&lt;br /&gt;
Use symlink install and only build the packages you changed:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
colcon build --symlink-install --packages-select &amp;lt;changed_package_1&amp;gt; &amp;lt;changed_package_2&amp;gt;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Robot shuts down while driving (high speed / acceleration)&#039;&#039;&#039;&lt;br /&gt;
If commanded speed or acceleration is too aggressive, the battery management system can cut power and both Raspberry Pis may shut down.&lt;br /&gt;
Reduce drive aggressiveness and avoid sudden acceleration spikes.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;System behaves inconsistently between main and aux Pi&#039;&#039;&#039;&lt;br /&gt;
This is often caused by the two Raspberry Pis having unsynchronised clocks.&lt;br /&gt;
Run this from the main Raspberry Pi:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Only one Raspberry Pi appears to boot&#039;&#039;&#039;&lt;br /&gt;
You must hold the power button for a few seconds so both Raspberry Pis start correctly.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Left battery does not engage after BMS cutoff&#039;&#039;&#039;&lt;br /&gt;
If the left battery turns off due to battery management protection, plug in the robot before booting again, otherwise that battery may not re-engage.&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Unstable wireless connection in ASTA&#039;&#039;&#039;&lt;br /&gt;
Wi-Fi in ASTA is often unreliable. Prefer connecting your PC over Ethernet for stable control and monitoring.&lt;br /&gt;
&lt;br /&gt;
=== Other utilities ===&lt;br /&gt;
&lt;br /&gt;
If you want to generate a square signal, you can use the node I made:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator --ros-args -p frequency:=&amp;lt;signal_freq&amp;gt; -p out_type:=&amp;lt;output ROS2 type&amp;gt; -r /sig_out:=&amp;lt;output topic&amp;gt; -p bias:=&amp;lt;signal bias&amp;gt; -p range:=&amp;lt;vmax-vmin&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typically you wight want to send it as if it was a gamepad input:&lt;br /&gt;
&lt;br /&gt;
 ros2 run arcana_tools signal_generator--ros-args -p frequency:=0.5 -p out_type:=geometry_msgs.msg.Twist.linear.x -r /sig_out:=/joy_control/cmd_vel -p range:=0.5&lt;br /&gt;
&lt;br /&gt;
You can display graphs using the rqt tool (type rqt in a terminal). There&#039;s a plugin &amp;quot;Vizualization&amp;gt;Plot&amp;quot; that integrate the python 3 matplotlib package.&lt;br /&gt;
&lt;br /&gt;
To enable the fetching of the wheels velocities from the bridge, you may need to edit the file &amp;quot;~/.config/fejemis_bridge/bridge.yaml&amp;quot; and set enable: true instead of false in the vel section.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Mechanics ===&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Mechanics]] in this page. &lt;br /&gt;
&lt;br /&gt;
=== Electronics ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Electronics]] in this page.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
The software is split into two components:&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis bridge]]&#039;&#039;&#039; — handles communication between the main Raspberry Pi and the Teensy controllers and provides a manual/remote-control interface. See [[Fejemis repository]] for source and docs.&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis ROS2 Software]]&#039;&#039;&#039; — higher-level programs for mapping, navigation, and robot behaviours located in &#039;&#039;&#039;Fejemis_workspace_2026&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Old repository ====&lt;br /&gt;
You can access the description of the legacy  through this link.&lt;br /&gt;
&lt;br /&gt;
== Mission Statement ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8955</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8955"/>
		<updated>2026-05-28T08:43:06Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Building and Recommendations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
=== Fields2Cover ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
=== ldlidar_stl_ros2 ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
=== opennav_coverage ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
=== raubase_core ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
=== ros_arcana (arcana_python, arcana_tools) ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
* Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
  colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
  colcon build --symlink-install&lt;br /&gt;
&lt;br /&gt;
* If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
* If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8954</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8954"/>
		<updated>2026-05-28T08:42:36Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Dependencies Index */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
=== Fields2Cover ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
=== ldlidar_stl_ros2 ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
=== opennav_coverage ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
=== raubase_core ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
=== ros_arcana (arcana_python, arcana_tools) ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
* Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
  colcon build --symlink-install&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
* If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8953</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8953"/>
		<updated>2026-05-28T08:41:52Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Fields2Cover */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
=== Fields2Cover ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
=== ldlidar_stl_ros2 ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
Notes: Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
=== opennav_coverage ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
Notes: Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
=== raubase_core ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
Notes: Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
=== ros_arcana (arcana_python, arcana_tools) ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
* Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
  colcon build --symlink-install&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
* If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8952</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8952"/>
		<updated>2026-05-28T08:41:35Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
=== Fields2Cover ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Purpose:&#039;&#039;&#039; Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
Notes: Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
=== ldlidar_stl_ros2 ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
Notes: Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
=== opennav_coverage ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
Notes: Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
=== raubase_core ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
Notes: Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
=== ros_arcana (arcana_python, arcana_tools) ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
* Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
  colcon build --symlink-install&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
* If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8951</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8951"/>
		<updated>2026-05-28T08:40:20Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Building and Recommendations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
=== Fields2Cover ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
Notes: Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
=== ldlidar_stl_ros2 ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
Notes: Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
=== opennav_coverage ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
Notes: Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
=== raubase_core ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
Notes: Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
=== ros_arcana (arcana_python, arcana_tools) ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
* Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
  colcon build --symlink-install&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
* If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8950</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8950"/>
		<updated>2026-05-28T08:39:19Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Dependencies Index */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
=== Fields2Cover ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
Notes: Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
=== ldlidar_stl_ros2 ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
Notes: Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
=== opennav_coverage ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
Notes: Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
=== raubase_core ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
Notes: Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
=== ros_arcana (arcana_python, arcana_tools) ===&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
- Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
colcon build --symlink-install&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
- If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
- If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;br /&gt;
&lt;br /&gt;
If you want, I can expand individual sections with example launch commands, config locations, and common troubleshooting notes.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8949</id>
		<title>Dependencies Index</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Dependencies_Index&amp;diff=8949"/>
		<updated>2026-05-28T08:38:40Z</updated>

		<summary type="html">&lt;p&gt;S253809: Created page with &amp;quot;Back to Fejemis ROS2 Software  == Dependencies Index ==  This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.  -- Fields2Cover  Purpose: Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.  Where used: ``opennav_coverage`` and Fejemis covera...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Dependencies Index ==&lt;br /&gt;
&lt;br /&gt;
This page summarises the main vendored dependencies under ``src/deps`` and their role in the Fejemis stack. The repository keeps these in-tree to avoid mismatched versions and to make development reproducible.&lt;br /&gt;
&lt;br /&gt;
-- Fields2Cover&lt;br /&gt;
&lt;br /&gt;
Purpose: Complete coverage path planning library providing swath generation, headland generation, route ordering, and path smoothing.&lt;br /&gt;
&lt;br /&gt;
Where used: ``opennav_coverage`` and Fejemis coverage planners.&lt;br /&gt;
&lt;br /&gt;
Notes: Prefer the workspace copy (``src/deps/Fields2Cover``) when building — installing a distro package can produce header/ABI conflicts.&lt;br /&gt;
&lt;br /&gt;
-- ldlidar_stl_ros2&lt;br /&gt;
&lt;br /&gt;
Purpose: Driver and adapter for the LD-series LiDAR hardware used on Fejemis. Publishes 2D scans used by SLAM and Nav2.&lt;br /&gt;
&lt;br /&gt;
Where used: ``fejemis_maploc`` LiDAR bringup and Nav2 costmaps.&lt;br /&gt;
&lt;br /&gt;
Notes: Launch configuration often exposes serial port and container placement arguments (see ``launch/lidar.launch.py``).&lt;br /&gt;
&lt;br /&gt;
-- opennav_coverage&lt;br /&gt;
&lt;br /&gt;
Purpose: Wraps Fields2Cover into ROS 2 lifecycle components and provides a Nav2-compatible Complete Coverage Task Server.&lt;br /&gt;
&lt;br /&gt;
Where used: Optional coverage navigation component integrated by ``fejemis_maploc`` when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
Notes: Depends on Fields2Cover; keep both workspace copies consistent.&lt;br /&gt;
&lt;br /&gt;
-- raubase_core&lt;br /&gt;
&lt;br /&gt;
Purpose: Core robot control messages, adapters, and helpers (includes ``RoverCommand``, ``rover_2_twist`` adapter, and mixers).&lt;br /&gt;
&lt;br /&gt;
Where used: Bridge and simulator control paths; adapters that keep simulation and real hardware command interfaces aligned.&lt;br /&gt;
&lt;br /&gt;
Notes: Building ``raubase_core`` alongside simulation ensures consistent message formats for Gazebo and the real robot.&lt;br /&gt;
&lt;br /&gt;
-- ros_arcana (arcana_python, arcana_tools)&lt;br /&gt;
&lt;br /&gt;
Purpose: Small utility libraries and launch helpers used across the workspace (xacro helpers, launch conveniences, python utilities).&lt;br /&gt;
&lt;br /&gt;
Where used: Launch utilities, xacro handling, and small tooling used by multiple packages (e.g., ``fejemis_sim`` uses arcana launch helpers).&lt;br /&gt;
&lt;br /&gt;
Notes: These packages are lightweight but important for consistent launch behaviour across machines and ROS distros.&lt;br /&gt;
&lt;br /&gt;
== Building and Recommendations ==&lt;br /&gt;
&lt;br /&gt;
- Build the workspace copy of deps first so downstream packages use the in-tree install. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
colcon build --symlink-install --packages-up-to fields2cover&lt;br /&gt;
colcon build --symlink-install&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
- If you previously installed a system package (for example ``ros-jazzy-fields2cover``), uninstall it if you want the workspace copy to be authoritative. Use your system package manager (``apt remove ros-jazzy-fields2cover``) with care; sudo is required.&lt;br /&gt;
&lt;br /&gt;
- If a build fails with missing headers, check that the workspace-installed ``fields2cover`` CMake config is being found (see CMakeCache or the ``build/*/CMakeFiles/CMakeConfigureLog.yaml`` entries).&lt;br /&gt;
&lt;br /&gt;
If you want, I can expand individual sections with example launch commands, config locations, and common troubleshooting notes.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8948</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8948"/>
		<updated>2026-05-28T08:38:13Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
In this page we focus on the ROS 2 software stack to learn more about the low-level microcontroller integration see [[Fejemis bridge]] and [[Fejemis repository]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[Fejemis Description ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo. For more detailed information see [[Fejemis Sim ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Dependencies (src/deps) ==&lt;br /&gt;
&lt;br /&gt;
The repository vendors a set of third-party and support packages under ``src/deps``. These are kept in-tree to ensure reproducible builds and known-good versions for Fejemis.&lt;br /&gt;
&lt;br /&gt;
For a short index and per-package summaries see [[Dependencies Index]].&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8947</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8947"/>
		<updated>2026-05-28T08:33:15Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
In this page we focus on the ROS 2 software stack to learn more about the low-level microcontroller integration see [[Fejemis bridge]] and [[Fejemis repository]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[Fejemis Description ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo. For more detailed information see [[Fejemis Sim ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Sim_ROS2&amp;diff=8946</id>
		<title>Fejemis Sim ROS2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Sim_ROS2&amp;diff=8946"/>
		<updated>2026-05-28T08:15:10Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Typical Usage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_sim&#039;&#039; contains the Gazebo Sim bringup for Fejemis. It provides the world files, model assets, ROS-GZ bridge configuration, and launch file used to spawn the robot in simulation with the same control interface as the real robot.&lt;br /&gt;
&lt;br /&gt;
The package is used to:&lt;br /&gt;
&lt;br /&gt;
* Load a selectable Gazebo world for development and testing.&lt;br /&gt;
* Publish the robot description from &#039;&#039;fejemis_description&#039;&#039; and spawn the model in Gazebo.&lt;br /&gt;
* Bridge robot state, sensors, and commands between ROS 2 and Gazebo.&lt;br /&gt;
* Optionally convert &#039;&#039;RoverCommand&#039;&#039; messages into Gazebo &#039;&#039;Twist&#039;&#039; commands so simulation matches the real control path.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;launch&#039;&#039; - simulation bringup entry point.&lt;br /&gt;
* &#039;&#039;config&#039;&#039; - Gazebo GUI configuration, bridge mappings, and RViz configuration.&lt;br /&gt;
* &#039;&#039;models&#039;&#039; - xacro fragments used to assemble Gazebo world templates.&lt;br /&gt;
* &#039;&#039;worlds&#039;&#039; - ready-made Gazebo worlds such as &#039;&#039;room.world&#039;&#039;, &#039;&#039;empty.world&#039;&#039;, and &#039;&#039;dynamic.world&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Main Launch File ==&lt;br /&gt;
&lt;br /&gt;
The package is centered around &#039;&#039;launch/sim.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch sequence:&lt;br /&gt;
&lt;br /&gt;
* Includes &#039;&#039;fejemis_description::launch/model.launch.py&#039;&#039; with &#039;&#039;use_sim_time=true&#039;&#039; so the robot description is available before spawning.&lt;br /&gt;
* Starts &#039;&#039;ros_gz_sim::launch/gz_sim.launch.py&#039;&#039; with the selected world file and &#039;&#039;config/gazebo.config&#039;&#039; GUI settings.&lt;br /&gt;
* Spawns the robot in Gazebo with &#039;&#039;ros_gz_sim/create&#039;&#039; using &#039;&#039;/robot_description&#039;&#039; and the Gazebo model name &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* Starts &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; in the &#039;&#039;fejemis&#039;&#039; namespace using &#039;&#039;config/gz_bridge.yaml&#039;&#039;.&lt;br /&gt;
* Optionally starts &#039;&#039;raubase_core/rover_2_twist&#039;&#039; when the rover-command adapter is enabled.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - selects the world to load. Default: &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables the RoverCommand-to-Twist adapter. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - declared for launch consistency and GUI workflows.&lt;br /&gt;
&lt;br /&gt;
== World Files ==&lt;br /&gt;
&lt;br /&gt;
The default world is &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;, which assembles the standard world template with the Fejemis simulation models.&lt;br /&gt;
&lt;br /&gt;
Available world files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;worlds/room.world&#039;&#039; - the main indoor test room used by the simulator.&lt;br /&gt;
* &#039;&#039;worlds/empty.world&#039;&#039; - a minimal empty world for lightweight testing.&lt;br /&gt;
* &#039;&#039;worlds/dynamic.world&#039;&#039; - a more active world variant for motion and interaction testing.&lt;br /&gt;
&lt;br /&gt;
The world templates are built from the xacro fragments in &#039;&#039;models/worlds&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Gazebo Bridge ==&lt;br /&gt;
&lt;br /&gt;
The bridge configuration in &#039;&#039;config/gz_bridge.yaml&#039;&#039; maps the robot command, state, and sensor topics between ROS 2 and Gazebo.&lt;br /&gt;
&lt;br /&gt;
Bridged topics:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039; to &#039;&#039;/model/fejemis/cmd_vel&#039;&#039; - command input from ROS to Gazebo.&lt;br /&gt;
* &#039;&#039;/clock&#039;&#039; - simulation time from Gazebo to ROS.&lt;br /&gt;
* &#039;&#039;/joint_states&#039;&#039; to &#039;&#039;/world/demo/model/fejemis/joint_state&#039;&#039; - joint state feedback.&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; to &#039;&#039;/model/fejemis/odom&#039;&#039; - simulated odometry.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; to &#039;&#039;/model/fejemis/imu&#039;&#039; - IMU output.&lt;br /&gt;
* &#039;&#039;/fejemis/mag&#039;&#039; to &#039;&#039;/model/fejemis/mag&#039;&#039; - magnetometer output.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; to &#039;&#039;/model/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/fejemis/image_raw&#039;&#039; to &#039;&#039;/model/fejemis/image_raw&#039;&#039; - camera image stream.&lt;br /&gt;
* &#039;&#039;/fejemis/camera_info&#039;&#039; to &#039;&#039;/model/fejemis/camera_info&#039;&#039; - camera calibration data.&lt;br /&gt;
&lt;br /&gt;
This bridge lets the simulator exercise the same downstream ROS 2 stack that runs on the real robot.&lt;br /&gt;
&lt;br /&gt;
== Robot Control Path ==&lt;br /&gt;
&lt;br /&gt;
By default, the simulator expects rover commands on &#039;&#039;/fejemis/cmd_vel&#039;&#039; in the &#039;&#039;raubase_core/msg/RoverCommand&#039;&#039; format. When &#039;&#039;rover_cmd_adapter&#039;&#039; is enabled, &#039;&#039;raubase_core/rover_2_twist&#039;&#039; converts that into &#039;&#039;geometry_msgs/Twist&#039;&#039; on &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039;, which is then forwarded to Gazebo.&lt;br /&gt;
&lt;br /&gt;
This keeps the simulation control interface aligned with the real Fejemis command pipeline.&lt;br /&gt;
&lt;br /&gt;
If the adapter is disabled, you can publish &#039;&#039;geometry_msgs/Twist&#039;&#039; directly to &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
&lt;br /&gt;
The package also ships an RViz configuration in &#039;&#039;config/rviz2.rviz&#039;&#039; for simulator debugging and model inspection.&lt;br /&gt;
&lt;br /&gt;
The Gazebo GUI settings are stored in &#039;&#039;config/gazebo.config&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Basic simulator launch:&lt;br /&gt;
  ros2 launch fejemis_sim sim.launch.py&lt;br /&gt;
Use a different world:&lt;br /&gt;
  ros2 launch fejemis_sim sim.launch.py world:=fejemis_sim::worlds/empty.world&lt;br /&gt;
Disable the rover-command adapter and publish Twist directly:&lt;br /&gt;
  ros2 launch fejemis_sim sim.launch.py rover_cmd_adapter:=False&lt;br /&gt;
&lt;br /&gt;
Example RoverCommand input:&lt;br /&gt;
  ros2 topic pub /fejemis/cmd_vel raubase_core/msg/RoverCommand &amp;quot;{source: 2, linear_vel: 0.2, turn_rate: 0.0}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Integration With The Rest Of The Stack ==&lt;br /&gt;
&lt;br /&gt;
The simulation package is usually started together with &#039;&#039;fejemis&#039;&#039; and &#039;&#039;fejemis_maploc&#039;&#039; when testing the full ROS 2 stack in a desktop or lab environment.&lt;br /&gt;
&lt;br /&gt;
In a simulated workflow, the same map, navigation, and behaviour layers can be exercised without running the physical bridge or Teensy hardware, while still keeping the robot description, command topics, and sensor topics aligned with the real system.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Sim_ROS2&amp;diff=8945</id>
		<title>Fejemis Sim ROS2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Sim_ROS2&amp;diff=8945"/>
		<updated>2026-05-28T08:14:29Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Typical Usage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_sim&#039;&#039; contains the Gazebo Sim bringup for Fejemis. It provides the world files, model assets, ROS-GZ bridge configuration, and launch file used to spawn the robot in simulation with the same control interface as the real robot.&lt;br /&gt;
&lt;br /&gt;
The package is used to:&lt;br /&gt;
&lt;br /&gt;
* Load a selectable Gazebo world for development and testing.&lt;br /&gt;
* Publish the robot description from &#039;&#039;fejemis_description&#039;&#039; and spawn the model in Gazebo.&lt;br /&gt;
* Bridge robot state, sensors, and commands between ROS 2 and Gazebo.&lt;br /&gt;
* Optionally convert &#039;&#039;RoverCommand&#039;&#039; messages into Gazebo &#039;&#039;Twist&#039;&#039; commands so simulation matches the real control path.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;launch&#039;&#039; - simulation bringup entry point.&lt;br /&gt;
* &#039;&#039;config&#039;&#039; - Gazebo GUI configuration, bridge mappings, and RViz configuration.&lt;br /&gt;
* &#039;&#039;models&#039;&#039; - xacro fragments used to assemble Gazebo world templates.&lt;br /&gt;
* &#039;&#039;worlds&#039;&#039; - ready-made Gazebo worlds such as &#039;&#039;room.world&#039;&#039;, &#039;&#039;empty.world&#039;&#039;, and &#039;&#039;dynamic.world&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Main Launch File ==&lt;br /&gt;
&lt;br /&gt;
The package is centered around &#039;&#039;launch/sim.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch sequence:&lt;br /&gt;
&lt;br /&gt;
* Includes &#039;&#039;fejemis_description::launch/model.launch.py&#039;&#039; with &#039;&#039;use_sim_time=true&#039;&#039; so the robot description is available before spawning.&lt;br /&gt;
* Starts &#039;&#039;ros_gz_sim::launch/gz_sim.launch.py&#039;&#039; with the selected world file and &#039;&#039;config/gazebo.config&#039;&#039; GUI settings.&lt;br /&gt;
* Spawns the robot in Gazebo with &#039;&#039;ros_gz_sim/create&#039;&#039; using &#039;&#039;/robot_description&#039;&#039; and the Gazebo model name &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* Starts &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; in the &#039;&#039;fejemis&#039;&#039; namespace using &#039;&#039;config/gz_bridge.yaml&#039;&#039;.&lt;br /&gt;
* Optionally starts &#039;&#039;raubase_core/rover_2_twist&#039;&#039; when the rover-command adapter is enabled.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - selects the world to load. Default: &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables the RoverCommand-to-Twist adapter. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - declared for launch consistency and GUI workflows.&lt;br /&gt;
&lt;br /&gt;
== World Files ==&lt;br /&gt;
&lt;br /&gt;
The default world is &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;, which assembles the standard world template with the Fejemis simulation models.&lt;br /&gt;
&lt;br /&gt;
Available world files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;worlds/room.world&#039;&#039; - the main indoor test room used by the simulator.&lt;br /&gt;
* &#039;&#039;worlds/empty.world&#039;&#039; - a minimal empty world for lightweight testing.&lt;br /&gt;
* &#039;&#039;worlds/dynamic.world&#039;&#039; - a more active world variant for motion and interaction testing.&lt;br /&gt;
&lt;br /&gt;
The world templates are built from the xacro fragments in &#039;&#039;models/worlds&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Gazebo Bridge ==&lt;br /&gt;
&lt;br /&gt;
The bridge configuration in &#039;&#039;config/gz_bridge.yaml&#039;&#039; maps the robot command, state, and sensor topics between ROS 2 and Gazebo.&lt;br /&gt;
&lt;br /&gt;
Bridged topics:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039; to &#039;&#039;/model/fejemis/cmd_vel&#039;&#039; - command input from ROS to Gazebo.&lt;br /&gt;
* &#039;&#039;/clock&#039;&#039; - simulation time from Gazebo to ROS.&lt;br /&gt;
* &#039;&#039;/joint_states&#039;&#039; to &#039;&#039;/world/demo/model/fejemis/joint_state&#039;&#039; - joint state feedback.&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; to &#039;&#039;/model/fejemis/odom&#039;&#039; - simulated odometry.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; to &#039;&#039;/model/fejemis/imu&#039;&#039; - IMU output.&lt;br /&gt;
* &#039;&#039;/fejemis/mag&#039;&#039; to &#039;&#039;/model/fejemis/mag&#039;&#039; - magnetometer output.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; to &#039;&#039;/model/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/fejemis/image_raw&#039;&#039; to &#039;&#039;/model/fejemis/image_raw&#039;&#039; - camera image stream.&lt;br /&gt;
* &#039;&#039;/fejemis/camera_info&#039;&#039; to &#039;&#039;/model/fejemis/camera_info&#039;&#039; - camera calibration data.&lt;br /&gt;
&lt;br /&gt;
This bridge lets the simulator exercise the same downstream ROS 2 stack that runs on the real robot.&lt;br /&gt;
&lt;br /&gt;
== Robot Control Path ==&lt;br /&gt;
&lt;br /&gt;
By default, the simulator expects rover commands on &#039;&#039;/fejemis/cmd_vel&#039;&#039; in the &#039;&#039;raubase_core/msg/RoverCommand&#039;&#039; format. When &#039;&#039;rover_cmd_adapter&#039;&#039; is enabled, &#039;&#039;raubase_core/rover_2_twist&#039;&#039; converts that into &#039;&#039;geometry_msgs/Twist&#039;&#039; on &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039;, which is then forwarded to Gazebo.&lt;br /&gt;
&lt;br /&gt;
This keeps the simulation control interface aligned with the real Fejemis command pipeline.&lt;br /&gt;
&lt;br /&gt;
If the adapter is disabled, you can publish &#039;&#039;geometry_msgs/Twist&#039;&#039; directly to &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
&lt;br /&gt;
The package also ships an RViz configuration in &#039;&#039;config/rviz2.rviz&#039;&#039; for simulator debugging and model inspection.&lt;br /&gt;
&lt;br /&gt;
The Gazebo GUI settings are stored in &#039;&#039;config/gazebo.config&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Basic simulator launch:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  ros2 launch fejemis_sim sim.launch.py&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Use a different world:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  ros2 launch fejemis_sim sim.launch.py world:=fejemis_sim::worlds/empty.world&lt;br /&gt;
&lt;br /&gt;
Disable the rover-command adapter and publish Twist directly:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 launch fejemis_sim sim.launch.py rover_cmd_adapter:=False&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Example RoverCommand input:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 topic pub /fejemis/cmd_vel raubase_core/msg/RoverCommand &amp;quot;{source: 2, linear_vel: 0.2, turn_rate: 0.0}&amp;quot;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Integration With The Rest Of The Stack ==&lt;br /&gt;
&lt;br /&gt;
The simulation package is usually started together with &#039;&#039;fejemis&#039;&#039; and &#039;&#039;fejemis_maploc&#039;&#039; when testing the full ROS 2 stack in a desktop or lab environment.&lt;br /&gt;
&lt;br /&gt;
In a simulated workflow, the same map, navigation, and behaviour layers can be exercised without running the physical bridge or Teensy hardware, while still keeping the robot description, command topics, and sensor topics aligned with the real system.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Sim_ROS2&amp;diff=8944</id>
		<title>Fejemis Sim ROS2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Sim_ROS2&amp;diff=8944"/>
		<updated>2026-05-28T08:13:54Z</updated>

		<summary type="html">&lt;p&gt;S253809: Created page with &amp;quot;Back to Fejemis ROS2 Software  == Purpose ==  &amp;#039;&amp;#039;fejemis_sim&amp;#039;&amp;#039; contains the Gazebo Sim bringup for Fejemis. It provides the world files, model assets, ROS-GZ bridge configuration, and launch file used to spawn the robot in simulation with the same control interface as the real robot.  The package is used to:  * Load a selectable Gazebo world for development and testing. * Publish the robot description from &amp;#039;&amp;#039;fejemis_description&amp;#039;&amp;#039; and spawn the model in Gazebo. * Bridg...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_sim&#039;&#039; contains the Gazebo Sim bringup for Fejemis. It provides the world files, model assets, ROS-GZ bridge configuration, and launch file used to spawn the robot in simulation with the same control interface as the real robot.&lt;br /&gt;
&lt;br /&gt;
The package is used to:&lt;br /&gt;
&lt;br /&gt;
* Load a selectable Gazebo world for development and testing.&lt;br /&gt;
* Publish the robot description from &#039;&#039;fejemis_description&#039;&#039; and spawn the model in Gazebo.&lt;br /&gt;
* Bridge robot state, sensors, and commands between ROS 2 and Gazebo.&lt;br /&gt;
* Optionally convert &#039;&#039;RoverCommand&#039;&#039; messages into Gazebo &#039;&#039;Twist&#039;&#039; commands so simulation matches the real control path.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;launch&#039;&#039; - simulation bringup entry point.&lt;br /&gt;
* &#039;&#039;config&#039;&#039; - Gazebo GUI configuration, bridge mappings, and RViz configuration.&lt;br /&gt;
* &#039;&#039;models&#039;&#039; - xacro fragments used to assemble Gazebo world templates.&lt;br /&gt;
* &#039;&#039;worlds&#039;&#039; - ready-made Gazebo worlds such as &#039;&#039;room.world&#039;&#039;, &#039;&#039;empty.world&#039;&#039;, and &#039;&#039;dynamic.world&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Main Launch File ==&lt;br /&gt;
&lt;br /&gt;
The package is centered around &#039;&#039;launch/sim.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch sequence:&lt;br /&gt;
&lt;br /&gt;
* Includes &#039;&#039;fejemis_description::launch/model.launch.py&#039;&#039; with &#039;&#039;use_sim_time=true&#039;&#039; so the robot description is available before spawning.&lt;br /&gt;
* Starts &#039;&#039;ros_gz_sim::launch/gz_sim.launch.py&#039;&#039; with the selected world file and &#039;&#039;config/gazebo.config&#039;&#039; GUI settings.&lt;br /&gt;
* Spawns the robot in Gazebo with &#039;&#039;ros_gz_sim/create&#039;&#039; using &#039;&#039;/robot_description&#039;&#039; and the Gazebo model name &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* Starts &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; in the &#039;&#039;fejemis&#039;&#039; namespace using &#039;&#039;config/gz_bridge.yaml&#039;&#039;.&lt;br /&gt;
* Optionally starts &#039;&#039;raubase_core/rover_2_twist&#039;&#039; when the rover-command adapter is enabled.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - selects the world to load. Default: &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables the RoverCommand-to-Twist adapter. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - declared for launch consistency and GUI workflows.&lt;br /&gt;
&lt;br /&gt;
== World Files ==&lt;br /&gt;
&lt;br /&gt;
The default world is &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;, which assembles the standard world template with the Fejemis simulation models.&lt;br /&gt;
&lt;br /&gt;
Available world files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;worlds/room.world&#039;&#039; - the main indoor test room used by the simulator.&lt;br /&gt;
* &#039;&#039;worlds/empty.world&#039;&#039; - a minimal empty world for lightweight testing.&lt;br /&gt;
* &#039;&#039;worlds/dynamic.world&#039;&#039; - a more active world variant for motion and interaction testing.&lt;br /&gt;
&lt;br /&gt;
The world templates are built from the xacro fragments in &#039;&#039;models/worlds&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Gazebo Bridge ==&lt;br /&gt;
&lt;br /&gt;
The bridge configuration in &#039;&#039;config/gz_bridge.yaml&#039;&#039; maps the robot command, state, and sensor topics between ROS 2 and Gazebo.&lt;br /&gt;
&lt;br /&gt;
Bridged topics:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039; to &#039;&#039;/model/fejemis/cmd_vel&#039;&#039; - command input from ROS to Gazebo.&lt;br /&gt;
* &#039;&#039;/clock&#039;&#039; - simulation time from Gazebo to ROS.&lt;br /&gt;
* &#039;&#039;/joint_states&#039;&#039; to &#039;&#039;/world/demo/model/fejemis/joint_state&#039;&#039; - joint state feedback.&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; to &#039;&#039;/model/fejemis/odom&#039;&#039; - simulated odometry.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; to &#039;&#039;/model/fejemis/imu&#039;&#039; - IMU output.&lt;br /&gt;
* &#039;&#039;/fejemis/mag&#039;&#039; to &#039;&#039;/model/fejemis/mag&#039;&#039; - magnetometer output.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; to &#039;&#039;/model/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/fejemis/image_raw&#039;&#039; to &#039;&#039;/model/fejemis/image_raw&#039;&#039; - camera image stream.&lt;br /&gt;
* &#039;&#039;/fejemis/camera_info&#039;&#039; to &#039;&#039;/model/fejemis/camera_info&#039;&#039; - camera calibration data.&lt;br /&gt;
&lt;br /&gt;
This bridge lets the simulator exercise the same downstream ROS 2 stack that runs on the real robot.&lt;br /&gt;
&lt;br /&gt;
== Robot Control Path ==&lt;br /&gt;
&lt;br /&gt;
By default, the simulator expects rover commands on &#039;&#039;/fejemis/cmd_vel&#039;&#039; in the &#039;&#039;raubase_core/msg/RoverCommand&#039;&#039; format. When &#039;&#039;rover_cmd_adapter&#039;&#039; is enabled, &#039;&#039;raubase_core/rover_2_twist&#039;&#039; converts that into &#039;&#039;geometry_msgs/Twist&#039;&#039; on &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039;, which is then forwarded to Gazebo.&lt;br /&gt;
&lt;br /&gt;
This keeps the simulation control interface aligned with the real Fejemis command pipeline.&lt;br /&gt;
&lt;br /&gt;
If the adapter is disabled, you can publish &#039;&#039;geometry_msgs/Twist&#039;&#039; directly to &#039;&#039;/fejemis/cmd_vel_twist&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
&lt;br /&gt;
The package also ships an RViz configuration in &#039;&#039;config/rviz2.rviz&#039;&#039; for simulator debugging and model inspection.&lt;br /&gt;
&lt;br /&gt;
The Gazebo GUI settings are stored in &#039;&#039;config/gazebo.config&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Basic simulator launch:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 launch fejemis_sim sim.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Use a different world:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 launch fejemis_sim sim.launch.py world:=fejemis_sim::worlds/empty.world&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Disable the rover-command adapter and publish Twist directly:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 launch fejemis_sim sim.launch.py rover_cmd_adapter:=False&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Example RoverCommand input:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
ros2 topic pub /fejemis/cmd_vel raubase_core/msg/RoverCommand &amp;quot;{source: 2, linear_vel: 0.2, turn_rate: 0.0}&amp;quot;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Integration With The Rest Of The Stack ==&lt;br /&gt;
&lt;br /&gt;
The simulation package is usually started together with &#039;&#039;fejemis&#039;&#039; and &#039;&#039;fejemis_maploc&#039;&#039; when testing the full ROS 2 stack in a desktop or lab environment.&lt;br /&gt;
&lt;br /&gt;
In a simulated workflow, the same map, navigation, and behaviour layers can be exercised without running the physical bridge or Teensy hardware, while still keeping the robot description, command topics, and sensor topics aligned with the real system.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8943</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8943"/>
		<updated>2026-05-28T08:06:49Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Fejemis Sim */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[Fejemis Description ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo. For more detailed information see [[Fejemis Sim ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8942</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8942"/>
		<updated>2026-05-28T08:06:23Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Fejemis Description */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[Fejemis Description ROS2]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8941</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8941"/>
		<updated>2026-05-28T08:06:07Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Fejemis Description */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model. Fore more detailed information see [[ROS2 Fejemis Description]]&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
== Network Configuration ==&lt;br /&gt;
&lt;br /&gt;
== ROS Network files ==&lt;br /&gt;
&lt;br /&gt;
The bash scripts used for configuring the ROS 2 network are located in the bin/ directory:&lt;br /&gt;
* &#039;&#039;bash_setup.sh&#039;&#039; - Configures the ROS 2 network settings for the Main Raspberry Pi&lt;br /&gt;
* &#039;&#039;bash_setup_aux.sh&#039;&#039; - Configures the ROS 2 network settings for the Aux Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
These scripts set up:&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
* CycloneDDS configuration&lt;br /&gt;
* Static peer discovery&lt;br /&gt;
* Network interface binding for Ethernet communication&lt;br /&gt;
&lt;br /&gt;
The scripts are automatically sourced during system startup, ensuring that the ROS 2 network is configured correctly whenever the Raspberry Pi units boot.&lt;br /&gt;
&lt;br /&gt;
Additional information about Ethernet configuration and the required ROS 2 network setup on external computers can be found in the [[Electronics]] section.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8940</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8940"/>
		<updated>2026-05-27T14:42:23Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Notes and Future Work */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 800px]]&lt;br /&gt;
&lt;br /&gt;
EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8939</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8939"/>
		<updated>2026-05-27T14:41:04Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* launch/tf.launch.py */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 800px]]&lt;br /&gt;
&lt;br /&gt;
EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8938</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8938"/>
		<updated>2026-05-27T14:40:25Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* launch/rtabmap_slam.launch.py */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 800px]]&lt;br /&gt;
&lt;br /&gt;
EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8937</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8937"/>
		<updated>2026-05-27T14:39:53Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* launch/odometry.launch.py */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 800px]]&lt;br /&gt;
&lt;br /&gt;
EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8936</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8936"/>
		<updated>2026-05-27T14:39:39Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* launch/odometry.launch.py */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 800px]]&lt;br /&gt;
EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8935</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8935"/>
		<updated>2026-05-27T14:39:20Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* launch/odometry.launch.py */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 800px]] EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8934</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8934"/>
		<updated>2026-05-27T14:38:45Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 1600px]] EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8933</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8933"/>
		<updated>2026-05-27T14:37:48Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:fejemis_maploc_ekf_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=File:Fejemis_maploc_ekf_block_diagram.png&amp;diff=8932</id>
		<title>File:Fejemis maploc ekf block diagram.png</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=File:Fejemis_maploc_ekf_block_diagram.png&amp;diff=8932"/>
		<updated>2026-05-27T14:35:48Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8930</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8930"/>
		<updated>2026-05-27T14:27:39Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Purpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
Overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8929</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8929"/>
		<updated>2026-05-27T14:27:09Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Purpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1600px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8927</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8927"/>
		<updated>2026-05-27T14:26:43Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Purpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 1200px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8926</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8926"/>
		<updated>2026-05-27T14:26:19Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Purpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
[[File:maploc_general_block_diagram.png| 600px]]&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=File:Maploc_general_block_diagram.png&amp;diff=8924</id>
		<title>File:Maploc general block diagram.png</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=File:Maploc_general_block_diagram.png&amp;diff=8924"/>
		<updated>2026-05-27T14:25:28Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8911</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8911"/>
		<updated>2026-05-27T14:01:41Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis ROS2 Software]]&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;fejemis_maploc&#039;&#039; is the mapping, localisation, navigation, and behaviour package for Fejemis. It connects the robot&#039;s sensor data to the navigation stack and exposes the high-level behaviour tree used to run calibration, mapping, waypoint navigation, and cleaning missions.&lt;br /&gt;
&lt;br /&gt;
The package sits between the low-level bridge and the high-level mission logic:&lt;br /&gt;
&lt;br /&gt;
* The bridge publishes wheel odometry, IMU data, and accepts velocity / brush commands.&lt;br /&gt;
* &#039;&#039;fejemis_maploc&#039;&#039; filters odometry, starts LiDAR and RealSense sensing, runs RTAB-Map, starts Nav2, and runs the Fejemis behaviour tree.&lt;br /&gt;
* The behaviour tree sends commands to Nav2, the coverage planner, the mixer, and the brush service.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; overall &#039;&#039;fejemis_maploc&#039;&#039; data flow from bridge, LiDAR, and RealSense into odometry, RTAB-Map, Nav2, coverage planning, behaviour tree, and robot commands.&lt;br /&gt;
&lt;br /&gt;
== Package Layout ==&lt;br /&gt;
&lt;br /&gt;
Important package folders:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;resources/launch&#039;&#039; - ROS 2 launch files for sensors, odometry, SLAM/localisation, planning, lifecycle managers, and behaviour trees.&lt;br /&gt;
* &#039;&#039;resources/config&#039;&#039; - YAML configuration for odometry, Nav2, RTAB-Map, RealSense, lifecycle managers, and BT plugins.&lt;br /&gt;
* &#039;&#039;resources/behavior&#039;&#039; - behaviour tree XML files. &#039;&#039;main.xml&#039;&#039; is the older BT.CPP format and &#039;&#039;main4.xml&#039;&#039; is the BT.CPP v4 version used on newer ROS distributions.&lt;br /&gt;
* &#039;&#039;scripts&#039;&#039; - Python helper nodes for mission triggering, RViz goal relays, brush services, velocity conversion, and coverage tests.&lt;br /&gt;
* &#039;&#039;src/bt_plugins&#039;&#039; - C++ behaviour tree plugin used by the cleaning behaviour.&lt;br /&gt;
&lt;br /&gt;
== Main Topics and Frames ==&lt;br /&gt;
&lt;br /&gt;
Common frame names:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;map&#039;&#039; - global map frame produced by RTAB-Map / Nav2.&lt;br /&gt;
* &#039;&#039;odom&#039;&#039; - local odometry frame.&lt;br /&gt;
* &#039;&#039;base_link&#039;&#039; - robot base frame.&lt;br /&gt;
* &#039;&#039;lidar_link&#039;&#039; - LiDAR frame.&lt;br /&gt;
* &#039;&#039;camera_camera_link&#039;&#039; and RealSense optical frames - camera frames used by the RealSense driver and RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Important topics and actions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/odom&#039;&#039; - wheel odometry from the bridge.&lt;br /&gt;
* &#039;&#039;/fejemis/imu&#039;&#039; and &#039;&#039;/fejemis/mag&#039;&#039; - raw IMU and magnetometer inputs.&lt;br /&gt;
* &#039;&#039;/loc/imu&#039;&#039; - filtered IMU output from Madgwick.&lt;br /&gt;
* &#039;&#039;/loc/odom&#039;&#039; - fused odometry output from the EKF.&lt;br /&gt;
* &#039;&#039;/fejemis/scan&#039;&#039; - LiDAR scan.&lt;br /&gt;
* &#039;&#039;/camera/color/image_raw&#039;&#039;, &#039;&#039;/camera/depth/image_rect_raw&#039;&#039;, and camera info topics - RealSense RGB-D input.&lt;br /&gt;
* &#039;&#039;/camera_scan&#039;&#039; - depth-image-to-laserscan output used as an extra obstacle source.&lt;br /&gt;
* &#039;&#039;/loc/map&#039;&#039; - occupancy map used by Nav2.&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - Nav2 velocity command before Fejemis-specific conversion.&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - velocity command sent into the existing Fejemis control path.&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039; - mission command topic consumed by the behaviour tree.&lt;br /&gt;
* &#039;&#039;/blackboard/...&#039;&#039; - blackboard values mirrored into the behaviour tree.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - Nav2 action used for waypoint goals.&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_complete_coverage&#039;&#039; - OpenNav coverage action used for coverage missions.&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039; and &#039;&#039;/control/brush/controller/off&#039;&#039; - brush control trigger services.&lt;br /&gt;
&lt;br /&gt;
== Launch Files ==&lt;br /&gt;
&lt;br /&gt;
The package is designed so the top-level &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039; can include only the parts needed on each Raspberry Pi. The main Pi typically handles bridge control and odometry, while the auxiliary Pi handles heavier sensing such as LiDAR, RealSense, RTAB-Map, and vision.&lt;br /&gt;
&lt;br /&gt;
=== launch/lidar.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the LD19 LiDAR driver from &#039;&#039;ldlidar_stl_ros2&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039; - whether the LiDAR launch should create a component container. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container&#039;&#039; - component container name. Default: &#039;&#039;_laser&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ld_container_ns&#039;&#039; - namespace for the component container. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR. Default: &#039;&#039;/dev/ttyUSB0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - launch argument declared for compatibility. The included launch currently receives &#039;&#039;fejemis&#039;&#039; as namespace.&lt;br /&gt;
&lt;br /&gt;
Important configuration passed to the driver:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;laser_frame=lidar_link&#039;&#039;&lt;br /&gt;
* &#039;&#039;clockwise_dir=false&#039;&#039;&lt;br /&gt;
* &#039;&#039;range_min=0.2&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The resulting scan is used by RTAB-Map and by the Nav2 costmaps as the main 2D obstacle source.&lt;br /&gt;
&lt;br /&gt;
=== launch/tf.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Publishes static transforms for the physical sensor layout.&lt;br /&gt;
&lt;br /&gt;
Published transforms:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;base_link -&amp;gt; camera_camera_link&#039;&#039; at approximately x=-0.24 m, y=0.0 m, z=0.44 m.&lt;br /&gt;
* On the real robot, &#039;&#039;base_link -&amp;gt; lidar_link&#039;&#039; at approximately x=-0.29 m, y=0.0 m, z=0.25 m, yaw=-1.5708 rad. The yaw correction compensates for the LiDAR&#039;s physical mounting angle.&lt;br /&gt;
* In simulation, &#039;&#039;laser_frame -&amp;gt; lidar_link&#039;&#039; is published instead, because Gazebo already provides &#039;&#039;laser_frame&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The launch file uses the &#039;&#039;in_sim&#039;&#039; launch configuration to select the real or simulated LiDAR transform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; TF tree showing &#039;&#039;map -&amp;gt; odom -&amp;gt; base_link&#039;&#039;, then the camera and LiDAR static frames.&lt;br /&gt;
&lt;br /&gt;
=== launch/odometry.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the odometry estimation path. On the real robot it runs both the Madgwick IMU filter and the robot_localization EKF. In simulation it only runs the EKF with the simulation-specific configuration.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulated odometry configuration. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; named &#039;&#039;imu_filter&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; named &#039;&#039;ekf&#039;&#039; in namespace &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Real robot remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;imu/mag -&amp;gt; /fejemis/mag&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data_raw -&amp;gt; /fejemis/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu/data -&amp;gt; imu&#039;&#039;, which resolves to &#039;&#039;/loc/imu&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/loc/odom_in -&amp;gt; /fejemis/odom&#039;&#039;&lt;br /&gt;
* &#039;&#039;odometry/filtered -&amp;gt; odom&#039;&#039;, which resolves to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* diagnostics are remapped to &#039;&#039;/loc/ekf/_diagnostics&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The EKF configuration in &#039;&#039;config/odometry.yaml&#039;&#039; runs at 30 Hz, uses two-dimensional mode, publishes TF, and fuses:&lt;br /&gt;
&lt;br /&gt;
* Wheel odometry from &#039;&#039;/fejemis/odom&#039;&#039;.&lt;br /&gt;
* RTAB-Map ICP odometry from &#039;&#039;/loc/rtabmap_odom&#039;&#039;.&lt;br /&gt;
* Filtered IMU input is present as &#039;&#039;/loc/imu&#039;&#039;, although the current real configuration disables the IMU state variables in the EKF and relies more heavily on odometry sources for motion estimation.&lt;br /&gt;
&lt;br /&gt;
Simulation mode uses &#039;&#039;config/odometry_sim.yaml&#039;&#039; and avoids the Madgwick IMU orientation because the simulated IMU orientation can conflict with Gazebo odometry frame conventions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; EKF input/output diagram: &#039;&#039;/fejemis/odom&#039;&#039;, &#039;&#039;/loc/rtabmap_odom&#039;&#039;, &#039;&#039;/loc/imu&#039;&#039; into &#039;&#039;/loc/ekf&#039;&#039;, output &#039;&#039;/loc/odom&#039;&#039; and TF.&lt;br /&gt;
&lt;br /&gt;
=== launch/realsense.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Includes the official &#039;&#039;realsense2_camera&#039;&#039; launch file with the package&#039;s &#039;&#039;config/realsense.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace for the camera. Default: empty string.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - declared for consistency, although the included launch is mainly configured through the RealSense launch arguments.&lt;br /&gt;
&lt;br /&gt;
Important RealSense settings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;publish_tf=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;enable_sync=true&#039;&#039;&lt;br /&gt;
* &#039;&#039;unite_imu_method=2&#039;&#039;&lt;br /&gt;
* &#039;&#039;config_file=fejemis_maploc/config/realsense.yaml&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The camera publishes the RGB, depth, camera info, IMU, and TF data used by RTAB-Map and by the depth-to-scan obstacle conversion.&lt;br /&gt;
&lt;br /&gt;
=== launch/rtabmap_slam.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the RTAB-Map mapping/localisation pipeline and includes &#039;&#039;tf.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - selects real or simulation config. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - if &#039;&#039;true&#039;&#039;, RTAB-Map loads an existing database and does not increment the map. If &#039;&#039;false&#039;&#039;, it starts in mapping mode and deletes the database on start. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for RTAB-Map. Default: &#039;&#039;loc&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the database path. &#039;&#039;lab&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap.db&#039;&#039; and &#039;&#039;asta&#039;&#039; maps to &#039;&#039;~/.ros/rtabmap_asta.db&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sensor remappings:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rgb/image -&amp;gt; /camera/color/image_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;rgb/camera_info -&amp;gt; /camera/color/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/image -&amp;gt; /camera/depth/image_rect_raw&#039;&#039;&lt;br /&gt;
* &#039;&#039;depth/camera_info -&amp;gt; /camera/depth/camera_info&#039;&#039;&lt;br /&gt;
* &#039;&#039;imu -&amp;gt; /loc/imu&#039;&#039;&lt;br /&gt;
* &#039;&#039;scan -&amp;gt; /fejemis/scan&#039;&#039;&lt;br /&gt;
* &#039;&#039;initialpose -&amp;gt; /initialpose&#039;&#039;&lt;br /&gt;
* &#039;&#039;grid_prob_map -&amp;gt; /map&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; converts the depth image into &#039;&#039;/camera_scan&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; synchronises RGB-D data on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; publishes ICP odometry to &#039;&#039;/loc/rtabmap_odom&#039;&#039; on the real robot.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; runs either SLAM mode or localisation mode.&lt;br /&gt;
&lt;br /&gt;
In SLAM mode, &#039;&#039;delete_db_on_start=True&#039;&#039; and RTAB-Map is launched with &#039;&#039;-d&#039;&#039;, so the selected database is overwritten. In localisation mode, &#039;&#039;Mem/IncrementalMemory=False&#039;&#039; and &#039;&#039;Mem/InitWMWithAllNodes=True&#039;&#039;, so the existing map is loaded for localisation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Block diagram placeholder:&#039;&#039;&#039; RTAB-Map data path from RealSense, LiDAR scan, EKF odom, and initial pose into RTAB-Map, with outputs to &#039;&#039;map&#039;&#039;, TF, and Nav2.&lt;br /&gt;
&lt;br /&gt;
=== launch/map.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts a component-based Nav2 map server in namespace &#039;&#039;loc&#039;&#039;, using &#039;&#039;config/nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the map server.&lt;br /&gt;
* &#039;&#039;map_make_container&#039;&#039;, &#039;&#039;map_container&#039;&#039;, and &#039;&#039;map_container_ns&#039;&#039; - declared as container-related options. The current launch creates a container named &#039;&#039;_map&#039;&#039; with prefix/namespace &#039;&#039;map&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The map server is configured with:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;topic_name=/loc/map&#039;&#039;&lt;br /&gt;
* &#039;&#039;frame_id=map&#039;&#039;&lt;br /&gt;
* &#039;&#039;yaml_filename=&amp;quot;&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In the current system, the map is primarily produced by RTAB-Map, while the Nav2 map server exposes map-server functionality for the navigation stack.&lt;br /&gt;
&lt;br /&gt;
=== launch/planning.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Nav2 planning and navigation servers, the Fejemis velocity relay, the brush service, and optionally the OpenNav coverage server.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace for Nav2 nodes. Default: &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to nodes. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables &#039;&#039;opennav_coverage&#039;&#039;. The default is detected from whether the coverage packages are installed.&lt;br /&gt;
&lt;br /&gt;
Nodes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039; named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_behaviors/behavior_server&#039;&#039; named &#039;&#039;behavior&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_planner/planner_server&#039;&#039; named &#039;&#039;planner_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; named &#039;&#039;bt_navigator&#039;&#039;.&lt;br /&gt;
* &#039;&#039;nav2_map_server/map_server&#039;&#039;.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; in namespace &#039;&#039;control/brush&#039;&#039;, named &#039;&#039;controller&#039;&#039;.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; named &#039;&#039;coverage_server&#039;&#039;, when enabled.&lt;br /&gt;
&lt;br /&gt;
Important remappings:&lt;br /&gt;
&lt;br /&gt;
* Nav2 odometry is remapped from &#039;&#039;/behavior/odom&#039;&#039; to &#039;&#039;/loc/odom&#039;&#039;.&lt;br /&gt;
* Nav2 map input is remapped from &#039;&#039;/behavior/map&#039;&#039; to &#039;&#039;/loc/map&#039;&#039;.&lt;br /&gt;
* Nav2 controller output &#039;&#039;cmd_vel&#039;&#039; is remapped to &#039;&#039;/control/nav2/cmd_vel&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The velocity relay then converts angular velocity units and republishes to &#039;&#039;/control/joy/cmd_vel&#039;&#039; for the existing Fejemis control path.&lt;br /&gt;
&lt;br /&gt;
The Nav2 configuration in &#039;&#039;config/nav2_servers.yaml&#039;&#039; uses:&lt;br /&gt;
&lt;br /&gt;
* Regulated Pure Pursuit as the path follower.&lt;br /&gt;
* Navfn as the global planner.&lt;br /&gt;
* Local costmap sources from LiDAR, camera scan, net scan, and cable scan.&lt;br /&gt;
* Global costmap with static map, LiDAR obstacle layer, and inflation layer.&lt;br /&gt;
* The OpenNav coverage navigator when coverage support is enabled.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts Nav2 lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - forwarded as &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_loc_lfc&#039;&#039; - controls whether the localisation lifecycle manager is started. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Lifecycle managers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/beh_lfc&#039;&#039; - behaviour/navigation lifecycle manager.&lt;br /&gt;
* &#039;&#039;/loc/loc_lfc&#039;&#039; - localisation lifecycle manager, only when &#039;&#039;enable_loc_lfc=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/fejemis/hard_lfc&#039;&#039; - hardware lifecycle manager, disabled in simulation.&lt;br /&gt;
&lt;br /&gt;
Diagnostics are remapped to &#039;&#039;/diag/beh_lfc&#039;&#039;, &#039;&#039;/diag/loc_lfc&#039;&#039;, and &#039;&#039;/diag/hard_lfc&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== launch/lifecycle_managers_only.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the navigation-side launch files without the heavy sensor components. This is useful when another machine is responsible for LiDAR, RealSense, or RTAB-Map.&lt;br /&gt;
&lt;br /&gt;
Launch arguments:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation time.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - when true, starts the planning stack on the main Pi. Default: &#039;&#039;false&#039;&#039;.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables coverage navigation. Default: &#039;&#039;true&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Included launch files:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;planning.launch.py&#039;&#039; is included only when &#039;&#039;in_sim=true&#039;&#039; or &#039;&#039;localization=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;lifecycle.launch.py&#039;&#039; is included immediately.&lt;br /&gt;
* &#039;&#039;behavior.launch.py&#039;&#039; is included after a 30 second delay, giving Nav2 servers time to become active before the BT engine connects to action servers.&lt;br /&gt;
&lt;br /&gt;
=== launch/behavior.launch.py ===&lt;br /&gt;
&lt;br /&gt;
Starts the Fejemis behaviour tree engine from &#039;&#039;raubase_core&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Launch argument:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - forwarded to the BT engine.&lt;br /&gt;
&lt;br /&gt;
The launch file selects the behaviour tree XML based on the ROS distribution:&lt;br /&gt;
&lt;br /&gt;
* Older BehaviourTree.CPP format: &#039;&#039;resources/behavior/main.xml&#039;&#039;.&lt;br /&gt;
* BehaviourTree.CPP v4 format: &#039;&#039;resources/behavior/main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node launched is:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;raubase_core/bt_engine&#039;&#039;, configured by &#039;&#039;config/bt_engine.yaml&#039;&#039; in namespace &#039;&#039;fejemis&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The BT plugin configuration loads:&lt;br /&gt;
&lt;br /&gt;
* raubase logging, mission, blackboard, coverage, and mixer plugins.&lt;br /&gt;
* Nav2 action plugins for single trigger, follow path, drive on heading, spin, and navigate to pose.&lt;br /&gt;
* OpenNav coverage path computation plugin.&lt;br /&gt;
* The package&#039;s custom &#039;&#039;fejemis_call_trigger_service_bt_node&#039;&#039; plugin.&lt;br /&gt;
&lt;br /&gt;
=== launch/robot.launch.py inclusion ===&lt;br /&gt;
&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package includes &#039;&#039;fejemis_maploc&#039;&#039; from &#039;&#039;fejemis/launch/robot.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Typical distribution:&lt;br /&gt;
&lt;br /&gt;
* Main Pi: bridge control, odometry, lifecycle managers, behaviour tree, and planning when localisation is enabled.&lt;br /&gt;
* Auxiliary Pi: LiDAR, RTAB-Map, RealSense, and optional vision.&lt;br /&gt;
&lt;br /&gt;
The top-level launch waits 3 seconds after bridge startup before launching the rest of the stack.&lt;br /&gt;
&lt;br /&gt;
== Script Nodes ==&lt;br /&gt;
&lt;br /&gt;
=== nav2_cmd_vel_deg_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: adapts Nav2 velocity commands to the Fejemis control convention.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/nav2/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity in rad/s.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/joy/cmd_vel&#039;&#039; - &#039;&#039;geometry_msgs/Twist&#039;&#039;, with angular velocity converted to deg/s.&lt;br /&gt;
&lt;br /&gt;
The linear fields are copied unchanged. The angular x, y, and z fields are multiplied by 180/pi. This is needed because Nav2 uses the normal ROS convention of rad/s, while the existing Fejemis bridge control path expects angular velocity in deg/s.&lt;br /&gt;
&lt;br /&gt;
=== brush_control_service.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: exposes simple ROS services for turning the brush on and off.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - topic for &#039;&#039;fejemis_bridge/msg/SteerManage&#039;&#039;. Default: &#039;&#039;/fejemis/front/manage&#039;&#039;.&lt;br /&gt;
* &#039;&#039;qos_depth&#039;&#039; - publisher QoS depth. Default: &#039;&#039;10&#039;&#039;.&lt;br /&gt;
* &#039;&#039;initial_brush_on&#039;&#039; - initial internal state. Default: &#039;&#039;False&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;manage_topic&#039;&#039; - publishes &#039;&#039;SteerManage.BRUSH_ON&#039;&#039; or &#039;&#039;SteerManage.BRUSH_OFF&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Services:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/set&#039;&#039; - &#039;&#039;std_srvs/SetBool&#039;&#039;, true means brush on and false means brush off.&lt;br /&gt;
* &#039;&#039;~/on&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush on.&lt;br /&gt;
* &#039;&#039;~/off&#039;&#039; - &#039;&#039;std_srvs/Trigger&#039;&#039;, turns the brush off.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;planning.launch.py&#039;&#039; this node runs as &#039;&#039;/control/brush/controller&#039;&#039;, which creates the services used by the behaviour tree:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== rviz_goal_relay.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: lets an operator send a Nav2 goal from RViz.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/goal_pose&#039;&#039; - RViz 2D Nav Goal as &#039;&#039;geometry_msgs/PoseStamped&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/fejemis/navigate_to_pose&#039;&#039; - &#039;&#039;nav2_msgs/action/NavigateToPose&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When a goal is clicked in RViz, the node checks that the action server is ready, forwards the goal, and logs whether it was accepted and whether it succeeded.&lt;br /&gt;
&lt;br /&gt;
=== mission_trigger.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: starts a cleaning mission by publishing blackboard values and then setting the active mission.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;mission&#039;&#039; - mission string to publish. Default: &#039;&#039;cleaning/waypoint&#039;&#039;.&lt;br /&gt;
* &#039;&#039;dock_pose&#039;&#039; - dock pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.62, 3.9, 0.0]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;waypoint_pose&#039;&#039; - waypoint pose as &#039;&#039;[x, y, yaw]&#039;&#039;. Default: &#039;&#039;[2.10491, 0.497438, 3.11127]&#039;&#039;.&lt;br /&gt;
* &#039;&#039;clean_poly&#039;&#039; - polygon string used for cleaning area blackboard values.&lt;br /&gt;
* &#039;&#039;startup_delay&#039;&#039; - delay before publishing. Default: &#039;&#039;2.0&#039;&#039; seconds.&lt;br /&gt;
&lt;br /&gt;
Publications:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/blackboard/dock_x&#039;&#039;, &#039;&#039;/blackboard/dock_y&#039;&#039;, &#039;&#039;/blackboard/dock_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_x&#039;&#039;, &#039;&#039;/blackboard/waypoint_y&#039;&#039;, &#039;&#039;/blackboard/waypoint_yaw&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/field_polygon&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/clean_poly&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/start_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/dock_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/blackboard/waypoint_pose&#039;&#039;&lt;br /&gt;
* &#039;&#039;/behavior/set_mission&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The blackboard publishers use transient-local QoS so late subscribers can still receive the latest values. Pose strings are encoded as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
stamp;frame;x;y;z;qx;qy;qz;qw&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The node also subscribes to &#039;&#039;/behavior/set_mission&#039;&#039;. When it sees &#039;&#039;auto/idle&#039;&#039; after the mission has run, it logs completion and shuts down.&lt;br /&gt;
&lt;br /&gt;
=== complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: interactive coverage test node for RViz.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;rviz_click_topic&#039;&#039; - clicked point topic. Default: &#039;&#039;/clicked_point&#039;&#039;.&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - number of clicked points before sending a goal. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Subscriptions:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/clicked_point&#039;&#039; by default - RViz point clicks.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, collects clicked points in RViz, converts them to a &#039;&#039;geometry_msgs/Polygon&#039;&#039;, and sends a coverage navigation action once enough points have been collected.&lt;br /&gt;
&lt;br /&gt;
=== fixed_complete_coverage_planner.py ===&lt;br /&gt;
&lt;br /&gt;
Purpose: fixed-polygon coverage test node.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;coverage_frame&#039;&#039; - frame for the coverage polygon. Default: &#039;&#039;map&#039;&#039;.&lt;br /&gt;
* &#039;&#039;min_polygon_points&#039;&#039; - retained from the interactive tester. Default: &#039;&#039;4&#039;&#039;.&lt;br /&gt;
* &#039;&#039;auto_close_polygon&#039;&#039; - whether to append the first point as the final point. Default: &#039;&#039;True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Action clients:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis/navigate_complete_coverage&#039;&#039; - &#039;&#039;opennav_coverage_msgs/action/NavigateCompleteCoverage&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node waits for &#039;&#039;fejemis/bt_navigator&#039;&#039; to become active, then sends a fixed four-point polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(-5.35,  2.45)&lt;br /&gt;
( 2.83,  3.18)&lt;br /&gt;
( 3.22, -5.08)&lt;br /&gt;
(-4.76, -5.97)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is useful for testing coverage planning without clicking points manually in RViz.&lt;br /&gt;
&lt;br /&gt;
== C++ Behaviour Tree Plugin ==&lt;br /&gt;
&lt;br /&gt;
=== fejemis_call_trigger_service_bt_node ===&lt;br /&gt;
&lt;br /&gt;
The C++ plugin &#039;&#039;src/bt_plugins/call_trigger_service_bt_node.cpp&#039;&#039; registers the behaviour tree node &#039;&#039;CallTriggerService&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;service_name&#039;&#039; - service to call.&lt;br /&gt;
* &#039;&#039;wait_for_service_ms&#039;&#039; - timeout while waiting for service availability. Default: &#039;&#039;1000&#039;&#039;.&lt;br /&gt;
* &#039;&#039;call_timeout_ms&#039;&#039; - timeout for the service call. Default: &#039;&#039;2000&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The node creates a &#039;&#039;std_srvs/Trigger&#039;&#039; client, waits for the target service, calls it, and returns:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SUCCESS&#039;&#039; when the service exists, responds before timeout, and returns &#039;&#039;success=true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;FAILURE&#039;&#039; when the service is unavailable, the call times out, or the response reports failure.&lt;br /&gt;
&lt;br /&gt;
The cleaning subtree uses it to call:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/control/brush/controller/on&#039;&#039;&lt;br /&gt;
* &#039;&#039;/control/brush/controller/off&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Behaviour Tree ==&lt;br /&gt;
&lt;br /&gt;
The active behaviour tree on newer ROS distributions is &#039;&#039;resources/behavior/main4.xml&#039;&#039;. It is loaded by &#039;&#039;behavior.launch.py&#039;&#039; through the &#039;&#039;raubase_core/bt_engine&#039;&#039; node.&lt;br /&gt;
&lt;br /&gt;
=== Main Loop ===&lt;br /&gt;
&lt;br /&gt;
The root tree is &#039;&#039;main&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
main&lt;br /&gt;
`-- Sequence&lt;br /&gt;
    |-- SingleTrigger&lt;br /&gt;
    |   `-- t_init&lt;br /&gt;
    `-- KeepRunningUntilFailure&lt;br /&gt;
        `-- ReactiveSequence&lt;br /&gt;
            |-- ForceSuccess&lt;br /&gt;
            |   `-- t_passive&lt;br /&gt;
            `-- t_runtime&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The structure means:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;t_init&#039;&#039; runs once at startup.&lt;br /&gt;
* &#039;&#039;t_passive&#039;&#039; runs repeatedly and updates blackboard/mission state.&lt;br /&gt;
* &#039;&#039;t_runtime&#039;&#039; selects the active behaviour based on the current mission.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Visualization placeholder:&#039;&#039;&#039; exported Groot image of &#039;&#039;main4.xml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_init ===&lt;br /&gt;
&lt;br /&gt;
Initialises the robot behaviour state:&lt;br /&gt;
&lt;br /&gt;
* Sets mission to &#039;&#039;auto/idle&#039;&#039;.&lt;br /&gt;
* Sets blackboard key &#039;&#039;initialized=1&#039;&#039;.&lt;br /&gt;
* Sets calibration square distance &#039;&#039;calib_sq_dist=7&#039;&#039;.&lt;br /&gt;
* Opens the mixer port using &#039;&#039;OpenMixerPort&#039;&#039; with priority 4 and topic &#039;&#039;/behavior&#039;&#039;.&lt;br /&gt;
* Logs that the robot is initialized.&lt;br /&gt;
&lt;br /&gt;
=== t_passive ===&lt;br /&gt;
&lt;br /&gt;
Runs as the passive safety/update loop:&lt;br /&gt;
&lt;br /&gt;
* Logs &#039;&#039;Safety loop&#039;&#039; at debug level.&lt;br /&gt;
* Refreshes &#039;&#039;start_pose&#039;&#039;, &#039;&#039;waypoint_pose&#039;&#039;, &#039;&#039;clean_poly&#039;&#039;, and &#039;&#039;field_polygon&#039;&#039; from &#039;&#039;/blackboard/...&#039;&#039; topics.&lt;br /&gt;
* Refreshes the current mission from &#039;&#039;/behavior/set_mission&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== t_runtime ===&lt;br /&gt;
&lt;br /&gt;
Selects behaviour using a reactive fallback:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t_runtime&lt;br /&gt;
|-- t_idle_runtime&lt;br /&gt;
|-- t_calib&lt;br /&gt;
|-- t_mapping&lt;br /&gt;
|-- t_cleaning&lt;br /&gt;
`-- unknown mission handler -&amp;gt; set idle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first matching mission branch runs. If no known branch matches, the tree logs an error and returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Idle ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_idle_runtime&#039;&#039; succeeds when the current mission is:&lt;br /&gt;
&lt;br /&gt;
* mission: &#039;&#039;auto&#039;&#039;&lt;br /&gt;
* mission_sub: &#039;&#039;idle&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This keeps the robot in a waiting state.&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_calib&#039;&#039; chooses between line calibration and square calibration.&lt;br /&gt;
&lt;br /&gt;
Line calibration, &#039;&#039;t_calib_line&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/line&#039;&#039;.&lt;br /&gt;
* Drives forward using &#039;&#039;DriveOnHeading&#039;&#039; for &#039;&#039;calib_sq_dist&#039;&#039; at 0.05 m/s.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Square calibration, &#039;&#039;t_calib_square&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;calib/*&#039;&#039;.&lt;br /&gt;
* Runs &#039;&#039;f_calib_side&#039;&#039; four times.&lt;br /&gt;
* Each side drives forward and spins 1.57 rad.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;t_mapping&#039;&#039; currently checks for mission &#039;&#039;mapping/*&#039;&#039; and then returns to idle. This branch is a placeholder for future autonomous mapping behaviour.&lt;br /&gt;
&lt;br /&gt;
=== Cleaning ===&lt;br /&gt;
&lt;br /&gt;
The current cleaning branch is &#039;&#039;t_cleaning&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* Requires mission &#039;&#039;cleaning/waypoint&#039;&#039; with mission_sub &#039;&#039;idle&#039;&#039;.&lt;br /&gt;
* Navigates to &#039;&#039;{waypoint_pose}&#039;&#039; using &#039;&#039;NavigateToPose&#039;&#039;.&lt;br /&gt;
* Sets current mission to &#039;&#039;cleaning/area&#039;&#039;.&lt;br /&gt;
* Returns to idle.&lt;br /&gt;
&lt;br /&gt;
Supporting cleaning subtrees are already present:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;f_get_area_to_clean&#039;&#039; sets a polygon and calls &#039;&#039;ComputeCoveragePath&#039;&#039;.&lt;br /&gt;
* &#039;&#039;f_clean_area&#039;&#039; turns the brush on, follows &#039;&#039;{cover_nav_path}&#039;&#039;, and turns the brush off.&lt;br /&gt;
* &#039;&#039;f_go_to_path_start&#039;&#039; is a placeholder that logs navigation to the path start.&lt;br /&gt;
&lt;br /&gt;
These subtrees show the intended full cleaning sequence, although the active &#039;&#039;t_cleaning&#039;&#039; branch currently only navigates to a waypoint and changes mission state.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Diagram placeholder:&#039;&#039;&#039; cleaning behaviour sequence: mission trigger -&amp;gt; waypoint navigation -&amp;gt; coverage path computation -&amp;gt; brush on -&amp;gt; follow coverage path -&amp;gt; brush off -&amp;gt; idle.&lt;br /&gt;
&lt;br /&gt;
== Configuration Files ==&lt;br /&gt;
&lt;br /&gt;
=== config/odometry.yaml and config/odometry_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure the EKF and IMU filter. The real configuration fuses wheel odometry and RTAB-Map ICP odometry in 2D mode. The simulation configuration is separated to avoid conflicting simulated IMU orientation.&lt;br /&gt;
&lt;br /&gt;
=== config/rtabmap.yaml and config/rtabmap_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure RTAB-Map for real robot and simulation use. &#039;&#039;rtabmap_slam.launch.py&#039;&#039; selects between them using &#039;&#039;use_sim_time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/nav2_servers.yaml and config/nav2_sim.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configure Nav2 controller, planner, behaviour server, costmaps, BT navigator, coverage server, and map server. The main real-robot launch uses &#039;&#039;nav2_servers.yaml&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== config/bt_engine.yaml ===&lt;br /&gt;
&lt;br /&gt;
Lists behaviour tree plugins loaded by &#039;&#039;raubase_core/bt_engine&#039;&#039;, including the Fejemis custom trigger-service plugin.&lt;br /&gt;
&lt;br /&gt;
=== config/lifecycle.yaml ===&lt;br /&gt;
&lt;br /&gt;
Configures lifecycle manager node lists and autostart behaviour for the navigation, localisation, and hardware lifecycle managers.&lt;br /&gt;
&lt;br /&gt;
=== config/realsense.yaml ===&lt;br /&gt;
&lt;br /&gt;
RealSense camera configuration loaded by &#039;&#039;realsense.launch.py&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Typical Usage ==&lt;br /&gt;
&lt;br /&gt;
Start the complete robot stack through the top-level launch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 launch fejemis robot.launch.py localization:=true map_location:=asta&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run on both Raspberry Pis. The launch file decides which parts run on the main and auxiliary computers through the &#039;&#039;is_aux&#039;&#039; argument.&lt;br /&gt;
&lt;br /&gt;
Start a waypoint cleaning mission after the system is up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc mission_trigger.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send an RViz navigation goal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc rviz_goal_relay.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with RViz clicked points:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test coverage navigation with the fixed polygon:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ros2 run fejemis_maploc fixed_complete_coverage_planner.py&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes and Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add exported diagrams for the odometry EKF, RTAB-Map data flow, Nav2 planning stack, and behaviour tree.&lt;br /&gt;
* Clarify which Raspberry Pi should run each launch file in the final deployment.&lt;br /&gt;
* Document the map database creation/update procedure for &#039;&#039;lab&#039;&#039; and &#039;&#039;asta&#039;&#039;.&lt;br /&gt;
* Expand the cleaning behaviour section once &#039;&#039;t_cleaning&#039;&#039; is connected to &#039;&#039;f_get_area_to_clean&#039;&#039; and &#039;&#039;f_clean_area&#039;&#039; in the active tree.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8903</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8903"/>
		<updated>2026-05-27T13:47:37Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= fejemis maploc =&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8902</id>
		<title>Fejemis Maploc Ros2</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_Maploc_Ros2&amp;diff=8902"/>
		<updated>2026-05-27T13:46:54Z</updated>

		<summary type="html">&lt;p&gt;S253809: Created page with &amp;quot;fejemis maploc&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;fejemis maploc&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Electronics&amp;diff=8901</id>
		<title>Electronics</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Electronics&amp;diff=8901"/>
		<updated>2026-05-27T13:45:31Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Realsense D455 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis 2026]]&lt;br /&gt;
&lt;br /&gt;
= Raspberry Pi =&lt;br /&gt;
&lt;br /&gt;
The robot uses two Raspberry Pi 5 units, referred to as the &amp;quot;Main&amp;quot; and &amp;quot;Aux&amp;quot; Raspberry Pi.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Main Pi&amp;quot; is responsible for core robot functionality and launches the odometry, lifecycle management, and bridge-related ROS nodes.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Aux Pi&amp;quot; handles the majority of the remaining ROS launch packages, including the computationally intensive perception and support nodes.&lt;br /&gt;
&lt;br /&gt;
=== Main Raspberry Pi ===&lt;br /&gt;
&lt;br /&gt;
* Raspberry Pi 5&lt;br /&gt;
* 16 GB RAM&lt;br /&gt;
* 60 GB storage (SD card)&lt;br /&gt;
&lt;br /&gt;
=== Aux Raspberry Pi ===&lt;br /&gt;
&lt;br /&gt;
* Raspberry Pi 5&lt;br /&gt;
* 8 GB RAM&lt;br /&gt;
* 512 GB external SSD storage&lt;br /&gt;
&lt;br /&gt;
== System Architecture ==&lt;br /&gt;
&lt;br /&gt;
During testing, it was observed that the Main Pi could not reliably handle all ROS launch packages simultaneously. To improve stability and performance, most workloads were therefore moved to the Aux Pi.&lt;br /&gt;
&lt;br /&gt;
One likely reason for this difference is the storage medium. The Main Pi uses an SD card, while the Aux Pi uses an external SSD, which provides significantly faster read/write performance and better handling of high I/O workloads generated by ROS 2 nodes, logging, and vision processing.&lt;br /&gt;
&lt;br /&gt;
This split architecture allows the robot to distribute computational load more effectively and improves overall system responsiveness and reliability.&lt;br /&gt;
&lt;br /&gt;
= Ethernet Network =&lt;br /&gt;
&lt;br /&gt;
== Router ==&lt;br /&gt;
The router is used to connect the Raspberry Pi units and external devices on the robot network. It allows communication between the onboard computers and provides Ethernet access for external computers during development, debugging, and monitoring.&lt;br /&gt;
&lt;br /&gt;
The router has a total of five Ethernet ports:&lt;br /&gt;
&lt;br /&gt;
* Two ports are currently occupied by the Main and Aux Raspberry Pi units&lt;br /&gt;
* The remaining ports can be used to connect external computers directly to the robot network&lt;br /&gt;
* Router power supply: 5V / 0.6A&lt;br /&gt;
&lt;br /&gt;
=== Network Configuration ===&lt;br /&gt;
&lt;br /&gt;
* Network/CIDR: 192.168.1.0/24&lt;br /&gt;
&lt;br /&gt;
* Subnet mask: 255.255.255.0&lt;br /&gt;
&lt;br /&gt;
=== Ethernet IP Addresses ===&lt;br /&gt;
&lt;br /&gt;
* Main Raspberry Pi: 192.168.1.1&lt;br /&gt;
* Aux Raspberry Pi: 192.168.1.2&lt;br /&gt;
&lt;br /&gt;
=== Setup for External Computers ===&lt;br /&gt;
&lt;br /&gt;
To connect a computer to the robot network through Ethernet, configure the network interface manually. The following settings must be configured:&lt;br /&gt;
* Subnet mask: 255.255.255.0&lt;br /&gt;
* Assign an available static IP address in the range 192.168.1.x&lt;br /&gt;
&lt;br /&gt;
Note: 192.168.1.1 and 192.168.1.2 are already assigned to the Raspberry Pi units and must not be reused&lt;br /&gt;
&lt;br /&gt;
=== ROS 2 Network Setup ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The ROS 2 network is configured so that the Raspberry Pi units and an external computer can communicate over the robot Ethernet network.&lt;br /&gt;
&lt;br /&gt;
The setup is already configured on the Raspberry Pi units. This includes:&lt;br /&gt;
&lt;br /&gt;
* Static Ethernet IP addresses&lt;br /&gt;
&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
&lt;br /&gt;
* CycloneDDS as the selected RMW implementation&lt;br /&gt;
&lt;br /&gt;
* CycloneDDS network interface configuration&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pi setup uses the following ROS 2 settings:&lt;br /&gt;
&lt;br /&gt;
 ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST&lt;br /&gt;
 RMW_IMPLEMENTATION=rmw_cyclonedds_cpp&lt;br /&gt;
 ROS_DOMAIN_ID=0&lt;br /&gt;
 CYCLONEDDS_URI=file://$HOME/.config/cyclonedds/cyclonedds.xml&lt;br /&gt;
&lt;br /&gt;
The CycloneDDS configuration binds ROS 2 communication to the Ethernet interface. This prevents ROS 2 from using the wrong network interface, such as Wi-Fi.&lt;br /&gt;
&lt;br /&gt;
=== Setup on External Computer ===&lt;br /&gt;
&lt;br /&gt;
To access ROS 2 topics from a local computer, the computer must be connected to the robot router through Ethernet.&lt;br /&gt;
&lt;br /&gt;
Configure the computer with a static IP address on the same subnet as the Raspberry Pi units as described in [[Setup for External Computers]].&lt;br /&gt;
&lt;br /&gt;
The following CycloneDDS configuration must be created on the local computer:&lt;br /&gt;
&lt;br /&gt;
File location:&lt;br /&gt;
 ~/.config/cyclonedds/cyclonedds.xml&lt;br /&gt;
&lt;br /&gt;
= Teensy Configuration =&lt;br /&gt;
&lt;br /&gt;
[[File:teensy-configuration.png | 600px]]&lt;br /&gt;
&lt;br /&gt;
Figure: The main hardware blocks. Two Teensy processors are the interface to the hardware. The drive processor controls the drive motors, safety and battery system. The front processor controls the brush and the front wheel to lift the brush. A main PC integrates the functionality with additional sensors to allow autonomous operation.&lt;br /&gt;
&lt;br /&gt;
= Teensy firmware =&lt;br /&gt;
&lt;br /&gt;
[[Fejemis Teensy]] software is build using standard Arduino library configurations.&lt;br /&gt;
The interface to the main PC is organized as text-lines.&lt;br /&gt;
&lt;br /&gt;
= Electrical =&lt;br /&gt;
&lt;br /&gt;
[[Fejemis electrical]] wiring etc.&lt;br /&gt;
&lt;br /&gt;
= Battery control =&lt;br /&gt;
&lt;br /&gt;
The [[Fejemis battery control]] is a 24V system (2x3cell LiPo 5Ah) with power on-off and measurement electronics. There is on-board chargers for all batteries.&lt;br /&gt;
&lt;br /&gt;
= Other features =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== LD19 LiDAR Sensor ===&lt;br /&gt;
&lt;br /&gt;
The LD19 is a 2D DTOF LiDAR sensor used for obstacle detection, mapping, and localization in the Fejemis platform.&lt;br /&gt;
&lt;br /&gt;
Technical specifications:&lt;br /&gt;
* Scanning range: 360°&lt;br /&gt;
* Scan frequency: 10 ± 0.1 Hz&lt;br /&gt;
* Sampling rate: 4500 measurements per second&lt;br /&gt;
* Communication interface: UART&lt;br /&gt;
* Baud rate: 230400 bit/s&lt;br /&gt;
* Supply voltage: 5V (4.5V–5.5V)&lt;br /&gt;
* Typical logic level: 3.3V&lt;br /&gt;
&lt;br /&gt;
More information and full documentation: https://www.elecrow.com/download/product/SLD06360F/LD19_Development%20Manual_V2.3.pdf&lt;br /&gt;
&lt;br /&gt;
=== Realsense D455 ===&lt;br /&gt;
&lt;br /&gt;
The Intel RealSense Depth Camera D455 provides RGB‑D sensing used for mapping, localization and obstacle detection. It is supported by the &#039;&#039;realsense2_camera&#039;&#039; ROS driver and integrates directly with the mapping components in this workspace (RTAB‑Map expects the standard RealSense RGB‑D topics).&lt;br /&gt;
Technical specifications:&lt;br /&gt;
* Operating range (min–max): 0.6 m – 6 m&lt;br /&gt;
* Depth resolution &amp;amp; FPS: 1280×720 up to 90 FPS&lt;br /&gt;
* Depth field of view (HxV): 86° × 57°&lt;br /&gt;
* Components: RGB sensor: Yes; Tracking module: Yes&lt;br /&gt;
* Module dimensions: 124 mm × 29 mm × 26 mm&lt;br /&gt;
* System interface type: USB 3.1&lt;br /&gt;
Product specifications and details: [https://www.intel.com/content/www/us/en/products/sku/205847/intel-realsense-depth-camera-d455/specifications.html Intel RealSense Depth Camera D455 - Specifications]&lt;br /&gt;
&lt;br /&gt;
=== Linear actuator ===&lt;br /&gt;
&lt;br /&gt;
The linear actuator used is a 12V DC actuator from Transmotec with a 10:1 gear ratio, 250 N maximum force, and a 150 mm stroke length.&lt;br /&gt;
&lt;br /&gt;
More information and full documentation: https://transmotec.com/product/DLA-12-10-A-150-HS2-IP65/&lt;br /&gt;
&lt;br /&gt;
=== Brush unit ===&lt;br /&gt;
&lt;br /&gt;
The Fejemis brush unit is a commercial brush that comes with its own battery (12V), charger and motor control. The unit is slightly modified to to allow measurement and control from the software.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Electronics&amp;diff=8899</id>
		<title>Electronics</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Electronics&amp;diff=8899"/>
		<updated>2026-05-27T13:44:59Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Realsense D455 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis 2026]]&lt;br /&gt;
&lt;br /&gt;
= Raspberry Pi =&lt;br /&gt;
&lt;br /&gt;
The robot uses two Raspberry Pi 5 units, referred to as the &amp;quot;Main&amp;quot; and &amp;quot;Aux&amp;quot; Raspberry Pi.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Main Pi&amp;quot; is responsible for core robot functionality and launches the odometry, lifecycle management, and bridge-related ROS nodes.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Aux Pi&amp;quot; handles the majority of the remaining ROS launch packages, including the computationally intensive perception and support nodes.&lt;br /&gt;
&lt;br /&gt;
=== Main Raspberry Pi ===&lt;br /&gt;
&lt;br /&gt;
* Raspberry Pi 5&lt;br /&gt;
* 16 GB RAM&lt;br /&gt;
* 60 GB storage (SD card)&lt;br /&gt;
&lt;br /&gt;
=== Aux Raspberry Pi ===&lt;br /&gt;
&lt;br /&gt;
* Raspberry Pi 5&lt;br /&gt;
* 8 GB RAM&lt;br /&gt;
* 512 GB external SSD storage&lt;br /&gt;
&lt;br /&gt;
== System Architecture ==&lt;br /&gt;
&lt;br /&gt;
During testing, it was observed that the Main Pi could not reliably handle all ROS launch packages simultaneously. To improve stability and performance, most workloads were therefore moved to the Aux Pi.&lt;br /&gt;
&lt;br /&gt;
One likely reason for this difference is the storage medium. The Main Pi uses an SD card, while the Aux Pi uses an external SSD, which provides significantly faster read/write performance and better handling of high I/O workloads generated by ROS 2 nodes, logging, and vision processing.&lt;br /&gt;
&lt;br /&gt;
This split architecture allows the robot to distribute computational load more effectively and improves overall system responsiveness and reliability.&lt;br /&gt;
&lt;br /&gt;
= Ethernet Network =&lt;br /&gt;
&lt;br /&gt;
== Router ==&lt;br /&gt;
The router is used to connect the Raspberry Pi units and external devices on the robot network. It allows communication between the onboard computers and provides Ethernet access for external computers during development, debugging, and monitoring.&lt;br /&gt;
&lt;br /&gt;
The router has a total of five Ethernet ports:&lt;br /&gt;
&lt;br /&gt;
* Two ports are currently occupied by the Main and Aux Raspberry Pi units&lt;br /&gt;
* The remaining ports can be used to connect external computers directly to the robot network&lt;br /&gt;
* Router power supply: 5V / 0.6A&lt;br /&gt;
&lt;br /&gt;
=== Network Configuration ===&lt;br /&gt;
&lt;br /&gt;
* Network/CIDR: 192.168.1.0/24&lt;br /&gt;
&lt;br /&gt;
* Subnet mask: 255.255.255.0&lt;br /&gt;
&lt;br /&gt;
=== Ethernet IP Addresses ===&lt;br /&gt;
&lt;br /&gt;
* Main Raspberry Pi: 192.168.1.1&lt;br /&gt;
* Aux Raspberry Pi: 192.168.1.2&lt;br /&gt;
&lt;br /&gt;
=== Setup for External Computers ===&lt;br /&gt;
&lt;br /&gt;
To connect a computer to the robot network through Ethernet, configure the network interface manually. The following settings must be configured:&lt;br /&gt;
* Subnet mask: 255.255.255.0&lt;br /&gt;
* Assign an available static IP address in the range 192.168.1.x&lt;br /&gt;
&lt;br /&gt;
Note: 192.168.1.1 and 192.168.1.2 are already assigned to the Raspberry Pi units and must not be reused&lt;br /&gt;
&lt;br /&gt;
=== ROS 2 Network Setup ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The ROS 2 network is configured so that the Raspberry Pi units and an external computer can communicate over the robot Ethernet network.&lt;br /&gt;
&lt;br /&gt;
The setup is already configured on the Raspberry Pi units. This includes:&lt;br /&gt;
&lt;br /&gt;
* Static Ethernet IP addresses&lt;br /&gt;
&lt;br /&gt;
* ROS 2 environment variables&lt;br /&gt;
&lt;br /&gt;
* CycloneDDS as the selected RMW implementation&lt;br /&gt;
&lt;br /&gt;
* CycloneDDS network interface configuration&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pi setup uses the following ROS 2 settings:&lt;br /&gt;
&lt;br /&gt;
 ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST&lt;br /&gt;
 RMW_IMPLEMENTATION=rmw_cyclonedds_cpp&lt;br /&gt;
 ROS_DOMAIN_ID=0&lt;br /&gt;
 CYCLONEDDS_URI=file://$HOME/.config/cyclonedds/cyclonedds.xml&lt;br /&gt;
&lt;br /&gt;
The CycloneDDS configuration binds ROS 2 communication to the Ethernet interface. This prevents ROS 2 from using the wrong network interface, such as Wi-Fi.&lt;br /&gt;
&lt;br /&gt;
=== Setup on External Computer ===&lt;br /&gt;
&lt;br /&gt;
To access ROS 2 topics from a local computer, the computer must be connected to the robot router through Ethernet.&lt;br /&gt;
&lt;br /&gt;
Configure the computer with a static IP address on the same subnet as the Raspberry Pi units as described in [[]].&lt;br /&gt;
&lt;br /&gt;
The following CycloneDDS configuration must be created on the local computer:&lt;br /&gt;
&lt;br /&gt;
File location:&lt;br /&gt;
 ~/.config/cyclonedds/cyclonedds.xml&lt;br /&gt;
&lt;br /&gt;
= Teensy Configuration =&lt;br /&gt;
&lt;br /&gt;
[[File:teensy-configuration.png | 600px]]&lt;br /&gt;
&lt;br /&gt;
Figure: The main hardware blocks. Two Teensy processors are the interface to the hardware. The drive processor controls the drive motors, safety and battery system. The front processor controls the brush and the front wheel to lift the brush. A main PC integrates the functionality with additional sensors to allow autonomous operation.&lt;br /&gt;
&lt;br /&gt;
= Teensy firmware =&lt;br /&gt;
&lt;br /&gt;
[[Fejemis Teensy]] software is build using standard Arduino library configurations.&lt;br /&gt;
The interface to the main PC is organized as text-lines.&lt;br /&gt;
&lt;br /&gt;
= Electrical =&lt;br /&gt;
&lt;br /&gt;
[[Fejemis electrical]] wiring etc.&lt;br /&gt;
&lt;br /&gt;
= Battery control =&lt;br /&gt;
&lt;br /&gt;
The [[Fejemis battery control]] is a 24V system (2x3cell LiPo 5Ah) with power on-off and measurement electronics. There is on-board chargers for all batteries.&lt;br /&gt;
&lt;br /&gt;
= Other features =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== LD19 LiDAR Sensor ===&lt;br /&gt;
&lt;br /&gt;
The LD19 is a 2D DTOF LiDAR sensor used for obstacle detection, mapping, and localization in the Fejemis platform.&lt;br /&gt;
&lt;br /&gt;
Technical specifications:&lt;br /&gt;
* Scanning range: 360°&lt;br /&gt;
* Scan frequency: 10 ± 0.1 Hz&lt;br /&gt;
* Sampling rate: 4500 measurements per second&lt;br /&gt;
* Communication interface: UART&lt;br /&gt;
* Baud rate: 230400 bit/s&lt;br /&gt;
* Supply voltage: 5V (4.5V–5.5V)&lt;br /&gt;
* Typical logic level: 3.3V&lt;br /&gt;
&lt;br /&gt;
More information and full documentation: https://www.elecrow.com/download/product/SLD06360F/LD19_Development%20Manual_V2.3.pdf&lt;br /&gt;
&lt;br /&gt;
=== Realsense D455 ===&lt;br /&gt;
&lt;br /&gt;
The Intel RealSense Depth Camera D455 provides RGB‑D sensing used for mapping, localization and obstacle detection. It is supported by the &#039;&#039;realsense2_camera&#039;&#039; ROS driver and integrates directly with the mapping components in this workspace (RTAB‑Map expects the standard RealSense RGB‑D topics).&lt;br /&gt;
Technical specifications:&lt;br /&gt;
* **Operating range (min–max):** 0.6 m – 6 m&lt;br /&gt;
* **Depth resolution &amp;amp; FPS:** 1280×720 up to 90 FPS&lt;br /&gt;
* **Depth field of view (HxV):** 86° × 57°&lt;br /&gt;
* **Components:** RGB sensor: Yes; Tracking module: Yes&lt;br /&gt;
* **Module dimensions:** 124 mm × 29 mm × 26 mm&lt;br /&gt;
* **System interface type:** USB 3.1&lt;br /&gt;
Product specifications and details: [https://www.intel.com/content/www/us/en/products/sku/205847/intel-realsense-depth-camera-d455/specifications.html Intel RealSense Depth Camera D455 - Specifications]&lt;br /&gt;
&lt;br /&gt;
=== Linear actuator ===&lt;br /&gt;
&lt;br /&gt;
The linear actuator used is a 12V DC actuator from Transmotec with a 10:1 gear ratio, 250 N maximum force, and a 150 mm stroke length.&lt;br /&gt;
&lt;br /&gt;
More information and full documentation: https://transmotec.com/product/DLA-12-10-A-150-HS2-IP65/&lt;br /&gt;
&lt;br /&gt;
=== Brush unit ===&lt;br /&gt;
&lt;br /&gt;
The Fejemis brush unit is a commercial brush that comes with its own battery (12V), charger and motor control. The unit is slightly modified to to allow measurement and control from the software.&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8895</id>
		<title>Fejemis 2026</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8895"/>
		<updated>2026-05-27T13:29:09Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Other utilities */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Fejemis is an autonomous cleaning robot developed to clean the Asta building. The building is divided into different cleaning areas, where the robot navigates autonomously to a selected point, plans a cleaning path, and performs the cleaning task using its brushing system while following the generated route.&lt;br /&gt;
&lt;br /&gt;
[[File:Fejemis.jpeg | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Start up guide ==&lt;br /&gt;
&lt;br /&gt;
To turn on Fejemis, you need to press the turn on button for more than 2 seconds. If not, both Raspberry Pis wont boot. To turn it off you need to press it for more than 7 seconds.&lt;br /&gt;
&lt;br /&gt;
To work with Fejemis, you can connect directly using a monitor and keyboard. The monitor can be connected to one of the Raspberry Pis, while the keyboard connects through USB. After booting, log into the Ubuntu system using the provided credentials.&lt;br /&gt;
&lt;br /&gt;
Fejemis uses two Raspberry Pis:&lt;br /&gt;
&lt;br /&gt;
Main Raspberry Pi and Auxiliary Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pis can be also accessed through SSH.(The IP some time changes, check it by connecting directly with the monitor)&lt;br /&gt;
 Main lab&lt;br /&gt;
 local@10.197.213.227&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.213.226&lt;br /&gt;
 Main asta&lt;br /&gt;
 local@10.197.219.238&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.219.237&lt;br /&gt;
&lt;br /&gt;
The last way of connecting is through Ethernet. &lt;br /&gt;
 Main&lt;br /&gt;
 197.168.1.1 &lt;br /&gt;
 Aux&lt;br /&gt;
 197.168.1.2&lt;br /&gt;
&lt;br /&gt;
If you are connected to the main Raspberry Pi,the auxiliary Raspberry Pi can also be accessed using:&lt;br /&gt;
 &lt;br /&gt;
 ssh fejemis-aux&lt;br /&gt;
&lt;br /&gt;
=== Launch===&lt;br /&gt;
&lt;br /&gt;
Launching the bridge (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge bridge.launch.py&lt;br /&gt;
&lt;br /&gt;
Launching the mixer + gamepad control (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge mixer.launch.py&lt;br /&gt;
&lt;br /&gt;
You need to press the &amp;quot;BACK&amp;quot; button on the gamepad to actually start the control from the gamepad.&lt;br /&gt;
&lt;br /&gt;
You can launch both at once with the following command: ros2 launch fejemis_bridge control.launch.py&lt;br /&gt;
&lt;br /&gt;
This launch file runs the latest version of the autonomous cleaning software:&lt;br /&gt;
&lt;br /&gt;
 ros2 launch fejemis robot.launch.py &lt;br /&gt;
&lt;br /&gt;
It must be launched on both Raspberry Pis, since each device handles different parts of the system.&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
 localization:=true/false, map_location:=asta/lab&lt;br /&gt;
&lt;br /&gt;
Default: localization true, map loc asta&lt;br /&gt;
&lt;br /&gt;
If the argument &amp;quot;localization:=false&amp;quot; is provided, the robot will automatically start a new mapping process and overwrite the current map.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After &#039;&#039;fejemis robot.launch.py&#039;&#039; is running on both Raspberry Pis, you can optionally start a few helper nodes depending on what you want to test:&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc rviz_goal_relay.py&amp;lt;/code&amp;gt; lets you send a navigation goal from RViz if you want to drive the robot to a specific point.&lt;br /&gt;
* If the robot does not localize itself automatically, you can set the initial pose in RViz.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc mission_trigger.py&amp;lt;/code&amp;gt; starts the cleaning behaviour once the robot is ready.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc fixed_complete_coverage_planner.py&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ros2 run fejemis_maploc complete_coverage_planner.py&amp;lt;/code&amp;gt; can be used to test only the cleaning path generation and coverage following.&lt;br /&gt;
&lt;br /&gt;
If Fejemis crash (one of the battery display will turn off), you need to plug it in before turning it on again.&lt;br /&gt;
&lt;br /&gt;
=== Other utilities ===&lt;br /&gt;
&lt;br /&gt;
If you want to generate a square signal, you can use the node I made:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator --ros-args -p frequency:=&amp;lt;signal_freq&amp;gt; -p out_type:=&amp;lt;output ROS2 type&amp;gt; -r /sig_out:=&amp;lt;output topic&amp;gt; -p bias:=&amp;lt;signal bias&amp;gt; -p range:=&amp;lt;vmax-vmin&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typically you wight want to send it as if it was a gamepad input:&lt;br /&gt;
&lt;br /&gt;
 ros2 run arcana_tools signal_generator--ros-args -p frequency:=0.5 -p out_type:=geometry_msgs.msg.Twist.linear.x -r /sig_out:=/joy_control/cmd_vel -p range:=0.5&lt;br /&gt;
&lt;br /&gt;
You can display graphs using the rqt tool (type rqt in a terminal). There&#039;s a plugin &amp;quot;Vizualization&amp;gt;Plot&amp;quot; that integrate the python 3 matplotlib package.&lt;br /&gt;
&lt;br /&gt;
To enable the fetching of the wheels velocities from the bridge, you may need to edit the file &amp;quot;~/.config/fejemis_bridge/bridge.yaml&amp;quot; and set enable: true instead of false in the vel section.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Mechanics ===&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Mechanics]] in this page. &lt;br /&gt;
&lt;br /&gt;
=== Electronics ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Electronics]] in this page.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
The software is split into two components:&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis bridge]]&#039;&#039;&#039; — handles communication between the main Raspberry Pi and the Teensy controllers and provides a manual/remote-control interface. See [[Fejemis repository]] for source and docs.&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis ROS2 Software]]&#039;&#039;&#039; — higher-level programs for mapping, navigation, and robot behaviours located in &#039;&#039;&#039;Fejemis_workspace_2026&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Old repository ====&lt;br /&gt;
You can access the description of the legacy  through this link.&lt;br /&gt;
&lt;br /&gt;
== Mission Statement ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8894</id>
		<title>Fejemis 2026</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8894"/>
		<updated>2026-05-27T13:28:57Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Other utilities */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Fejemis is an autonomous cleaning robot developed to clean the Asta building. The building is divided into different cleaning areas, where the robot navigates autonomously to a selected point, plans a cleaning path, and performs the cleaning task using its brushing system while following the generated route.&lt;br /&gt;
&lt;br /&gt;
[[File:Fejemis.jpeg | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Start up guide ==&lt;br /&gt;
&lt;br /&gt;
To turn on Fejemis, you need to press the turn on button for more than 2 seconds. If not, both Raspberry Pis wont boot. To turn it off you need to press it for more than 7 seconds.&lt;br /&gt;
&lt;br /&gt;
To work with Fejemis, you can connect directly using a monitor and keyboard. The monitor can be connected to one of the Raspberry Pis, while the keyboard connects through USB. After booting, log into the Ubuntu system using the provided credentials.&lt;br /&gt;
&lt;br /&gt;
Fejemis uses two Raspberry Pis:&lt;br /&gt;
&lt;br /&gt;
Main Raspberry Pi and Auxiliary Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pis can be also accessed through SSH.(The IP some time changes, check it by connecting directly with the monitor)&lt;br /&gt;
 Main lab&lt;br /&gt;
 local@10.197.213.227&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.213.226&lt;br /&gt;
 Main asta&lt;br /&gt;
 local@10.197.219.238&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.219.237&lt;br /&gt;
&lt;br /&gt;
The last way of connecting is through Ethernet. &lt;br /&gt;
 Main&lt;br /&gt;
 197.168.1.1 &lt;br /&gt;
 Aux&lt;br /&gt;
 197.168.1.2&lt;br /&gt;
&lt;br /&gt;
If you are connected to the main Raspberry Pi,the auxiliary Raspberry Pi can also be accessed using:&lt;br /&gt;
 &lt;br /&gt;
 ssh fejemis-aux&lt;br /&gt;
&lt;br /&gt;
=== Launch===&lt;br /&gt;
&lt;br /&gt;
Launching the bridge (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge bridge.launch.py&lt;br /&gt;
&lt;br /&gt;
Launching the mixer + gamepad control (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge mixer.launch.py&lt;br /&gt;
&lt;br /&gt;
You need to press the &amp;quot;BACK&amp;quot; button on the gamepad to actually start the control from the gamepad.&lt;br /&gt;
&lt;br /&gt;
You can launch both at once with the following command: ros2 launch fejemis_bridge control.launch.py&lt;br /&gt;
&lt;br /&gt;
This launch file runs the latest version of the autonomous cleaning software:&lt;br /&gt;
&lt;br /&gt;
 ros2 launch fejemis robot.launch.py &lt;br /&gt;
&lt;br /&gt;
It must be launched on both Raspberry Pis, since each device handles different parts of the system.&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
 localization:=true/false, map_location:=asta/lab&lt;br /&gt;
&lt;br /&gt;
Default: localization true, map loc asta&lt;br /&gt;
&lt;br /&gt;
If the argument &amp;quot;localization:=false&amp;quot; is provided, the robot will automatically start a new mapping process and overwrite the current map.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After &#039;&#039;fejemis robot.launch.py&#039;&#039; is running on both Raspberry Pis, you can optionally start a few helper nodes depending on what you want to test:&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc rviz_goal_relay.py&amp;lt;/code&amp;gt; lets you send a navigation goal from RViz if you want to drive the robot to a specific point.&lt;br /&gt;
* If the robot does not localize itself automatically, you can set the initial pose in RViz.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc mission_trigger.py&amp;lt;/code&amp;gt; starts the cleaning behaviour once the robot is ready.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc fixed_complete_coverage_planner.py&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ros2 run fejemis_maploc complete_coverage_planner.py&amp;lt;/code&amp;gt; can be used to test only the cleaning path generation and coverage following.&lt;br /&gt;
&lt;br /&gt;
If Fejemis crash (one of the battery display will turn off), you need to plug it in before turning it on again.&lt;br /&gt;
&lt;br /&gt;
=== Other utilities ===&lt;br /&gt;
&lt;br /&gt;
If you want to generate a square signal, you can use the node I made:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator --ros-args -p frequency:=&amp;lt;signal_freq&amp;gt; -p out_type:=&amp;lt;output ROS2 type&amp;gt; -r /sig_out:=&amp;lt;output topic&amp;gt; -p bias:=&amp;lt;signal bias&amp;gt; -p range:=&amp;lt;vmax-vmin&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typically you wight want to send it as if it was a gamepad input:&lt;br /&gt;
&lt;br /&gt;
  ros2 run arcana_tools signal_generator--ros-args -p frequency:=0.5 -p out_type:=geometry_msgs.msg.Twist.linear.x -r /sig_out:=/joy_control/cmd_vel -p range:=0.5&lt;br /&gt;
&lt;br /&gt;
You can display graphs using the rqt tool (type rqt in a terminal). There&#039;s a plugin &amp;quot;Vizualization&amp;gt;Plot&amp;quot; that integrate the python 3 matplotlib package.&lt;br /&gt;
&lt;br /&gt;
To enable the fetching of the wheels velocities from the bridge, you may need to edit the file &amp;quot;~/.config/fejemis_bridge/bridge.yaml&amp;quot; and set enable: true instead of false in the vel section.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Mechanics ===&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Mechanics]] in this page. &lt;br /&gt;
&lt;br /&gt;
=== Electronics ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Electronics]] in this page.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
The software is split into two components:&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis bridge]]&#039;&#039;&#039; — handles communication between the main Raspberry Pi and the Teensy controllers and provides a manual/remote-control interface. See [[Fejemis repository]] for source and docs.&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis ROS2 Software]]&#039;&#039;&#039; — higher-level programs for mapping, navigation, and robot behaviours located in &#039;&#039;&#039;Fejemis_workspace_2026&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Old repository ====&lt;br /&gt;
You can access the description of the legacy  through this link.&lt;br /&gt;
&lt;br /&gt;
== Mission Statement ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8893</id>
		<title>Fejemis 2026</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_2026&amp;diff=8893"/>
		<updated>2026-05-27T13:28:08Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Fejemis is an autonomous cleaning robot developed to clean the Asta building. The building is divided into different cleaning areas, where the robot navigates autonomously to a selected point, plans a cleaning path, and performs the cleaning task using its brushing system while following the generated route.&lt;br /&gt;
&lt;br /&gt;
[[File:Fejemis.jpeg | 600px]]&lt;br /&gt;
&lt;br /&gt;
== Start up guide ==&lt;br /&gt;
&lt;br /&gt;
To turn on Fejemis, you need to press the turn on button for more than 2 seconds. If not, both Raspberry Pis wont boot. To turn it off you need to press it for more than 7 seconds.&lt;br /&gt;
&lt;br /&gt;
To work with Fejemis, you can connect directly using a monitor and keyboard. The monitor can be connected to one of the Raspberry Pis, while the keyboard connects through USB. After booting, log into the Ubuntu system using the provided credentials.&lt;br /&gt;
&lt;br /&gt;
Fejemis uses two Raspberry Pis:&lt;br /&gt;
&lt;br /&gt;
Main Raspberry Pi and Auxiliary Raspberry Pi&lt;br /&gt;
&lt;br /&gt;
The Raspberry Pis can be also accessed through SSH.(The IP some time changes, check it by connecting directly with the monitor)&lt;br /&gt;
 Main lab&lt;br /&gt;
 local@10.197.213.227&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.213.226&lt;br /&gt;
 Main asta&lt;br /&gt;
 local@10.197.219.238&lt;br /&gt;
 Aux lab&lt;br /&gt;
 local@10.197.219.237&lt;br /&gt;
&lt;br /&gt;
The last way of connecting is through Ethernet. &lt;br /&gt;
 Main&lt;br /&gt;
 197.168.1.1 &lt;br /&gt;
 Aux&lt;br /&gt;
 197.168.1.2&lt;br /&gt;
&lt;br /&gt;
If you are connected to the main Raspberry Pi,the auxiliary Raspberry Pi can also be accessed using:&lt;br /&gt;
 &lt;br /&gt;
 ssh fejemis-aux&lt;br /&gt;
&lt;br /&gt;
=== Launch===&lt;br /&gt;
&lt;br /&gt;
Launching the bridge (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge bridge.launch.py&lt;br /&gt;
&lt;br /&gt;
Launching the mixer + gamepad control (from anywhere):&lt;br /&gt;
 ros2 launch fejemis_bridge mixer.launch.py&lt;br /&gt;
&lt;br /&gt;
You need to press the &amp;quot;BACK&amp;quot; button on the gamepad to actually start the control from the gamepad.&lt;br /&gt;
&lt;br /&gt;
You can launch both at once with the following command: ros2 launch fejemis_bridge control.launch.py&lt;br /&gt;
&lt;br /&gt;
This launch file runs the latest version of the autonomous cleaning software:&lt;br /&gt;
&lt;br /&gt;
 ros2 launch fejemis robot.launch.py &lt;br /&gt;
&lt;br /&gt;
It must be launched on both Raspberry Pis, since each device handles different parts of the system.&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
 localization:=true/false, map_location:=asta/lab&lt;br /&gt;
&lt;br /&gt;
Default: localization true, map loc asta&lt;br /&gt;
&lt;br /&gt;
If the argument &amp;quot;localization:=false&amp;quot; is provided, the robot will automatically start a new mapping process and overwrite the current map.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After &#039;&#039;fejemis robot.launch.py&#039;&#039; is running on both Raspberry Pis, you can optionally start a few helper nodes depending on what you want to test:&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc rviz_goal_relay.py&amp;lt;/code&amp;gt; lets you send a navigation goal from RViz if you want to drive the robot to a specific point.&lt;br /&gt;
* If the robot does not localize itself automatically, you can set the initial pose in RViz.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc mission_trigger.py&amp;lt;/code&amp;gt; starts the cleaning behaviour once the robot is ready.&lt;br /&gt;
* &amp;lt;code&amp;gt;ros2 run fejemis_maploc fixed_complete_coverage_planner.py&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ros2 run fejemis_maploc complete_coverage_planner.py&amp;lt;/code&amp;gt; can be used to test only the cleaning path generation and coverage following.&lt;br /&gt;
&lt;br /&gt;
If Fejemis crash (one of the battery display will turn off), you need to plug it in before turning it on again.&lt;br /&gt;
&lt;br /&gt;
=== Other utilities ===&lt;br /&gt;
&lt;br /&gt;
If you want to generate a square signal, you can use the node I made:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator --ros-args -p frequency:=&amp;lt;signal_freq&amp;gt; -p out_type:=&amp;lt;output ROS2 type&amp;gt; -r /sig_out:=&amp;lt;output topic&amp;gt; -p bias:=&amp;lt;signal bias&amp;gt; -p range:=&amp;lt;vmax-vmin&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typically you wight want to send it as if it was a gamepad input:&lt;br /&gt;
&lt;br /&gt;
ros2 run arcana_tools signal_generator--ros-args -p frequency:=0.5 -p out_type:=geometry_msgs.msg.Twist.linear.x -r /sig_out:=/joy_control/cmd_vel -p range:=0.5&lt;br /&gt;
&lt;br /&gt;
You can display graphs using the rqt tool (type rqt in a terminal). There&#039;s a plugin &amp;quot;Vizualization&amp;gt;Plot&amp;quot; that integrate the python 3 matplotlib package.&lt;br /&gt;
&lt;br /&gt;
To enable the fetching of the wheels velocities from the bridge, you may need to edit the file &amp;quot;~/.config/fejemis_bridge/bridge.yaml&amp;quot; and set enable: true instead of false in the vel section.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Mechanics ===&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Mechanics]] in this page. &lt;br /&gt;
&lt;br /&gt;
=== Electronics ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can access the technical description of the [[Electronics]] in this page.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
The software is split into two components:&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis bridge]]&#039;&#039;&#039; — handles communication between the main Raspberry Pi and the Teensy controllers and provides a manual/remote-control interface. See [[Fejemis repository]] for source and docs.&lt;br /&gt;
*&#039;&#039;&#039;[[Fejemis ROS2 Software]]&#039;&#039;&#039; — higher-level programs for mapping, navigation, and robot behaviours located in &#039;&#039;&#039;Fejemis_workspace_2026&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Old repository ====&lt;br /&gt;
You can access the description of the legacy  through this link.&lt;br /&gt;
&lt;br /&gt;
== Mission Statement ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8891</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8891"/>
		<updated>2026-05-27T13:18:06Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
* &#039;&#039;keyboard_teleop_rover.py&#039;&#039; - keyboard teleoperation node that publishes RoverCommand messages.&lt;br /&gt;
* &#039;&#039;twist_to_rover_command.py&#039;&#039; - adapter that converts Twist commands into RoverCommand messages.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/complete_coverage_planner.py&#039;&#039; and &#039;&#039;fejemis_maploc/fixed_complete_coverage_planner.py&#039;&#039; - coverage planners for generating full-field coverage actions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/mission_trigger.py&#039;&#039; - publishes the cleaning mission and supporting blackboard values.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/rviz_goal_relay.py&#039;&#039; - converts RViz goal clicks into NavigateToPose actions.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
== Network Configuration ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8886</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8886"/>
		<updated>2026-05-27T13:11:21Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together. See [[Fejemis Maploc Ros2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here. See [[Fejemis Vision ROS2]] for more detailed information.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
== Network Configuration ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8884</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8884"/>
		<updated>2026-05-27T13:10:04Z</updated>

		<summary type="html">&lt;p&gt;S253809: /* Fejemis Bridge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control. See [[Fejemis Bridge ROS2]] for more detailed information&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
== Network Configuration ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8877</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8877"/>
		<updated>2026-05-27T12:55:14Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Fejemis_2026]]&lt;br /&gt;
&lt;br /&gt;
== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
== Network Configuration ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8876</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8876"/>
		<updated>2026-05-27T12:46:28Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
The &#039;&#039;fejemis_maploc&#039;&#039; package contains the mapping, localisation, navigation, and sensor bringup for the robot. It is the package that ties the RealSense camera, LiDAR, odometry, RTAB-Map, Nav2, and the behaviour-tree layer together.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/lidar.launch.py&#039;&#039; - starts the LiDAR driver and forwards the serial-port and container settings.&lt;br /&gt;
* &#039;&#039;launch/tf.launch.py&#039;&#039; - publishes the static transforms between the robot, camera, and LiDAR frames.&lt;br /&gt;
* &#039;&#039;launch/odometry.launch.py&#039;&#039; - launches the EKF and IMU filter for wheel and IMU odometry.&lt;br /&gt;
* &#039;&#039;launch/map.launch.py&#039;&#039; - starts the map server component for the local map container.&lt;br /&gt;
* &#039;&#039;launch/lifecycle.launch.py&#039;&#039; - starts the Nav2 lifecycle managers.&lt;br /&gt;
* &#039;&#039;launch/planning.launch.py&#039;&#039; - starts the Nav2 planning stack, path follower, behavior server, and coverage server.&lt;br /&gt;
* &#039;&#039;launch/behavior.launch.py&#039;&#039; - starts the BT engine and behavior-tree-related runtime nodes.&lt;br /&gt;
* &#039;&#039;launch/realsense.launch.py&#039;&#039; - starts the RealSense camera driver.&lt;br /&gt;
* &#039;&#039;launch/rtabmap_slam.launch.py&#039;&#039; - starts RTAB-Map SLAM or localisation.&lt;br /&gt;
&lt;br /&gt;
Main nodes and components:&lt;br /&gt;
* &#039;&#039;imu_filter_madgwick_node&#039;&#039; - filters raw IMU data before fusion.&lt;br /&gt;
* &#039;&#039;robot_localization/ekf_node&#039;&#039; - fuses wheel odometry and IMU data.&lt;br /&gt;
* &#039;&#039;ldlidar_stl_ros2&#039;&#039; LiDAR driver - publishes the laser scan used by navigation and SLAM.&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the fixed camera and LiDAR transforms.&lt;br /&gt;
* &#039;&#039;depthimage_to_laserscan_node&#039;&#039; - converts the depth image to a laser scan for the costmaps.&lt;br /&gt;
* &#039;&#039;rtabmap_sync/rgbd_sync&#039;&#039; and &#039;&#039;rtabmap_odom/icp_odometry&#039;&#039; - prepare RGB-D data and odometry for RTAB-Map.&lt;br /&gt;
* &#039;&#039;rtabmap_slam/rtabmap&#039;&#039; - runs SLAM or localisation depending on the launch arguments.&lt;br /&gt;
* &#039;&#039;nav2_map_server::MapServer&#039;&#039; - provides the occupancy grid map.&lt;br /&gt;
* &#039;&#039;nav2_controller/controller_server&#039;&#039;, &#039;&#039;nav2_planner/planner_server&#039;&#039;, &#039;&#039;nav2_behaviors/behavior_server&#039;&#039;, and &#039;&#039;nav2_bt_navigator/bt_navigator&#039;&#039; - navigation and behaviour nodes.&lt;br /&gt;
* &#039;&#039;nav2_lifecycle_manager/lifecycle_manager&#039;&#039; - manages the Nav2 lifecycle transitions.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/nav2_cmd_vel_deg_relay.py&#039;&#039; - relays Nav2 velocity commands into the robot control format.&lt;br /&gt;
* &#039;&#039;fejemis_maploc/brush_control_service.py&#039;&#039; - exposes the brush control service used by the planning stack.&lt;br /&gt;
* &#039;&#039;opennav_coverage/opennav_coverage&#039;&#039; - optional coverage server when the coverage packages are available.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - switches the stack to simulated time.&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - used by the top-level bringup to decide which side of the multi-Pi setup launches the sensor stack.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - selects between SLAM mode and localisation mode in RTAB-Map.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects which RTAB-Map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack from the top-level bringup.&lt;br /&gt;
* &#039;&#039;enable_coverage&#039;&#039; - enables the coverage navigation components.&lt;br /&gt;
* &#039;&#039;namespace&#039;&#039; - namespace used by the SLAM and navigation nodes.&lt;br /&gt;
* &#039;&#039;port_name&#039;&#039; - serial port for the LiDAR.&lt;br /&gt;
* &#039;&#039;ld_make_container&#039;&#039;, &#039;&#039;ld_container&#039;&#039;, and &#039;&#039;ld_container_ns&#039;&#039; - control how the LiDAR node is placed in its component container.&lt;br /&gt;
* &#039;&#039;camera_namespace&#039;&#039; - namespace passed to the RealSense driver.&lt;br /&gt;
* &#039;&#039;world&#039;&#039;, &#039;&#039;rover_cmd_adapter&#039;&#039;, and &#039;&#039;rviz&#039;&#039; - simulation parameters forwarded from the top-level bringup.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
The &#039;&#039;fejemis_vision&#039;&#039; package provides the vision pipeline for the robot. In the current bringup, only the nodes launched from &#039;&#039;launch/main.launch.py&#039;&#039; are listed here.&lt;br /&gt;
&lt;br /&gt;
Nodes launched in &#039;&#039;launch/main.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;tf2_ros/static_transform_publisher&#039;&#039; - publishes the test camera transform when both &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;test_static_tf&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;rviz2/rviz2&#039;&#039; - opens RViz with the vision configuration when &#039;&#039;enable_vision&#039;&#039; and &#039;&#039;open_rviz&#039;&#039; are true.&lt;br /&gt;
* &#039;&#039;fejemis_vision/main.py&#039;&#039; - runs the main vision node when &#039;&#039;enable_vision&#039;&#039; is true.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - master switch for the vision stack.&lt;br /&gt;
* &#039;&#039;open_rviz&#039;&#039; - opens RViz for the vision stack when enabled.&lt;br /&gt;
* &#039;&#039;test_static_tf&#039;&#039; - adds a temporary static transform for camera frame testing.&lt;br /&gt;
* The main vision node also loads &#039;&#039;config/main.yaml&#039;&#039; and uses &#039;&#039;use_sim_time=False&#039;&#039; in this launch file.&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
== Network Configuration ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
	<entry>
		<id>https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8875</id>
		<title>Fejemis ROS2 Software</title>
		<link rel="alternate" type="text/html" href="https://rsewiki.electro.dtu.dk/index.php?title=Fejemis_ROS2_Software&amp;diff=8875"/>
		<updated>2026-05-27T12:41:57Z</updated>

		<summary type="html">&lt;p&gt;S253809: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Fejemis Software Overview ==&lt;br /&gt;
The Fejemis software is split into multiple components. The full ROS2 workspace and related repositories are available on GitHub: https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
This documentation summarises the main software components used on the robot and explains how the system can be distributed across multiple Raspberry Pis (Multi‑Pi setup).&lt;br /&gt;
&lt;br /&gt;
The two primary concerns are:&lt;br /&gt;
* Hardware bridge and low-level microcontroller integration (the Fejemis bridge).&lt;br /&gt;
* The ROS 2 software stack for mapping, perception, and high-level behaviours (the Fejemis ROS2 workspace).&lt;br /&gt;
&lt;br /&gt;
== Fejemis ==&lt;br /&gt;
The top-level &#039;&#039;fejemis&#039;&#039; package is the entry point for the robot stack. It ties the bridge, description, map/localisation, simulation, and vision packages together and installs the main launch files for the complete system.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/robot.launch.py&#039;&#039; - main bringup file for the real robot and multi-Pi setup. It starts the bridge control first and then launches odometry, LiDAR, SLAM, localisation, and vision depending on the selected options.&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - convenience entry point for simulation. It starts &#039;&#039;launch/robot.launch.py&#039;&#039; with &#039;&#039;in_sim=True&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters in &#039;&#039;launch/robot.launch.py&#039;&#039;:&lt;br /&gt;
* &#039;&#039;is_aux&#039;&#039; - marks whether the launch is running on the auxiliary Raspberry Pi. The default is detected automatically.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; - enables simulation mode.&lt;br /&gt;
* &#039;&#039;localization&#039;&#039; - runs RTAB-Map in localisation mode when &#039;&#039;true&#039;&#039;.&lt;br /&gt;
* &#039;&#039;map_location&#039;&#039; - selects the map database to load (&#039;&#039;lab&#039;&#039; or &#039;&#039;asta&#039;&#039;).&lt;br /&gt;
* &#039;&#039;enable_vision&#039;&#039; - enables the vision stack for people, cable, and net detection.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Bridge ==&lt;br /&gt;
The &#039;&#039;fejemis_bridge&#039;&#039; package handles the low-level interface between the main Raspberry Pi and the Teensy controllers. It contains the serial bridge, the command mixer, and the joystick helper nodes used for manual control.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/bridge.launch.py&#039;&#039; - starts the bridge node itself.&lt;br /&gt;
* &#039;&#039;launch/mixer.launch.py&#039;&#039; - starts the command mixer and joystick input nodes.&lt;br /&gt;
* &#039;&#039;launch/control.launch.py&#039;&#039; - launches the full control side of the bridge stack and forwards the serial-device arguments.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;bridge&#039;&#039; - main bridge executable, published in the &#039;&#039;fejemis&#039;&#039; namespace.&lt;br /&gt;
* &#039;&#039;raubase_core/rcmixer&#039;&#039; - command mixer that combines control inputs.&lt;br /&gt;
* &#039;&#039;joy/game_controller_node&#039;&#039; - joystick/gamepad input node.&lt;br /&gt;
* &#039;&#039;raubase_core/joy_control&#039;&#039; - joystick mode switch node.&lt;br /&gt;
* &#039;&#039;joy_brush_toggle.py&#039;&#039; - button toggle for the brush control.&lt;br /&gt;
* &#039;&#039;joy_linear_actuator.py&#039;&#039; - button toggle for the linear actuator.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;front_device&#039;&#039; - serial device for the front Teensy controller.&lt;br /&gt;
* &#039;&#039;drive_device&#039;&#039; - serial device for the drive Teensy controller.&lt;br /&gt;
* &#039;&#039;log_level&#039;&#039; - logging level for the bridge node.&lt;br /&gt;
* &#039;&#039;in_sim&#039;&#039; and &#039;&#039;is_aux&#039;&#039; - forwarded through the control launch file to select the correct runtime path.&lt;br /&gt;
* Joystick helper settings such as &#039;&#039;joy_topic&#039;&#039;, &#039;&#039;steer_manage_topic&#039;&#039;, &#039;&#039;linear_actuator_topic&#039;&#039;, &#039;&#039;button_index&#039;&#039;, &#039;&#039;initial_brush_on&#039;&#039;, and &#039;&#039;initial_actuator_up&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Description ==&lt;br /&gt;
The &#039;&#039;fejemis_description&#039;&#039; package provides the robot model and URDF/Xacro setup. It is responsible for publishing the robot description and state so the rest of the stack can use the same model.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/model.launch.py&#039;&#039; - generates the URDF from the Xacro description and launches &#039;&#039;robot_state_publisher&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;robot_state_publisher&#039;&#039; - publishes the robot TF tree from the generated &#039;&#039;robot_description&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;use_sim_time&#039;&#039; - enables simulated time when running in Gazebo or another simulator.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Sim ==&lt;br /&gt;
The &#039;&#039;fejemis_sim&#039;&#039; package contains the Gazebo simulation resources and the simulation bringup. It combines the robot description with the simulation world and connects ROS topics to Gazebo.&lt;br /&gt;
&lt;br /&gt;
Launch files:&lt;br /&gt;
* &#039;&#039;launch/sim.launch.py&#039;&#039; - starts the full simulation stack.&lt;br /&gt;
&lt;br /&gt;
Nodes in this package:&lt;br /&gt;
* &#039;&#039;ros_gz_sim/create&#039;&#039; - spawns the robot in Gazebo from &#039;&#039;/robot_description&#039;&#039;.&lt;br /&gt;
* &#039;&#039;ros_gz_bridge/parameter_bridge&#039;&#039; - bridges ROS and Gazebo topics.&lt;br /&gt;
* &#039;&#039;raubase_core/rover_2_twist&#039;&#039; - optional adapter that converts rover commands into Twist commands for Gazebo.&lt;br /&gt;
&lt;br /&gt;
Configurable parameters:&lt;br /&gt;
* &#039;&#039;world&#039;&#039; - world file to load, defaulting to &#039;&#039;fejemis_sim::worlds/room.world&#039;&#039;.&lt;br /&gt;
* &#039;&#039;rover_cmd_adapter&#039;&#039; - enables or disables the RoverCommand-to-Twist adapter.&lt;br /&gt;
* &#039;&#039;rviz&#039;&#039; - optional RViz flag provided by the launch file.&lt;br /&gt;
&lt;br /&gt;
== Fejemis Maploc ==&lt;br /&gt;
&lt;br /&gt;
== Fejemis Vision ==&lt;br /&gt;
&lt;br /&gt;
== Multi pi setup ==&lt;br /&gt;
This system supports a multi‑Pi arrangement where some hardware (for example, a RealSense camera or other sensors) runs on an auxiliary Raspberry Pi while the main robot and ROS 2 stack run on the primary Pi.&lt;br /&gt;
&lt;br /&gt;
Recommended minimal steps for a Multi‑Pi setup:&lt;br /&gt;
&lt;br /&gt;
1. Network and host setup&lt;br /&gt;
* Ensure all Pis are on the same local network (switch, VLAN or Wi‑Fi SSID) and can reach each other via IP.&lt;br /&gt;
* Prefer DHCP reservations or static IPs for each Pi to simplify configuration.&lt;br /&gt;
&lt;br /&gt;
2. SSH and automation&lt;br /&gt;
* Install SSH keys on each Pi so you can run remote commands and automation without passwords.&lt;br /&gt;
&lt;br /&gt;
3. Clone the workspace and install dependencies&lt;br /&gt;
On each Pi that needs software from the repository, clone the workspace and run the provided install scripts:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
git clone https://github.com/Melvin-Angel/Fejemis_Workspace_2026.git&lt;br /&gt;
cd Fejemis_Workspace_2026&lt;br /&gt;
./install.sh&lt;br /&gt;
source ./activate.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
4. Camera / sensor hosts&lt;br /&gt;
* If the camera is on an auxiliary Pi, run the camera driver or streaming node there and expose the expected ROS topics across the network.&lt;br /&gt;
* In this workspace the mapping components expect RealSense RGB‑D topics; when the camera is on an aux Pi you can launch the camera remotely. Example (run on the primary Pi to start the aux launch):&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis_maploc realsense.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
5. Time sync and reliability&lt;br /&gt;
* Enable NTP or systemd‑timesyncd on all Pis so timestamps on messages line up for logging and perception.&lt;br /&gt;
&lt;br /&gt;
=== Convenience commands ===&lt;br /&gt;
These helper commands make working with a two‑Pi setup easier.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis launch_aux&#039;&#039; — start a launch file on an auxiliary Pi from the primary Pi. This is commonly used to start camera or sensor nodes remotely. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis launch_aux fejemis robot.launch.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;fejemis sync_clocks&#039;&#039; — synchronise system clocks across Pis (uses SSH and sudo on targets). Run this before recording or starting perception stacks so timestamps align. Example:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
fejemis sync_clocks&lt;br /&gt;
```&lt;br /&gt;
== Network Configuration ==&lt;/div&gt;</summary>
		<author><name>S253809</name></author>
	</entry>
</feed>