### Install Project Executables and Resources Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_gazebo/CMakeLists.txt Installs the built executables to the appropriate library directory and copies launch files, world models, URDF models, and configuration files to the share directory of the installed package. This ensures the ROS 2 nodes and associated resources are accessible. ```cmake install(TARGETS motors_command_parser_node odometry_node ground_truth_remapper_node DESTINATION lib/${PROJECT_NAME} ) install( DIRECTORY launch worlds models rviz config DESTINATION share/${PROJECT_NAME} ) ament_package() ``` -------------------------------- ### Install ros2_rover Project Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md This snippet shows the shell commands required to clone the ros2_rover repository into a ROS 2 workspace, install its dependencies, and build the project. ```shell cd ~/ros2_ws/src git clone https://github.com/mgonzs13/ros2_rover cd ~/ros2_ws rosdep install --from-paths src -r -y colcon build ``` -------------------------------- ### Installation Configuration Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_motor_controller_cpp/CMakeLists.txt Configures the installation of the built executables ('controller_node', 'vel_parser_node') into the 'lib/ros2_rover' directory and the 'launch' directory into 'share/ros2_rover'. ```cmake # INSTALL install(TARGETS controller_node vel_parser_node DESTINATION lib/${PROJECT_NAME}) # install the launch directory install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}/ ) ``` -------------------------------- ### Install Linux Systemd Service Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Installs the rover as a systemd service for automatic startup on boot. This involves copying the service file, enabling it, and providing commands for service management and log viewing. ```bash # Install rover as Linux service cd ~/ros2_ws/src/ros2_rover/rover_service sudo ./install.sh # Service management commands sudo service rover start # Start rover sudo service rover stop # Stop rover sudo service rover restart # Restart rover sudo service rover status # Check status # Enable/disable automatic startup sudo systemctl enable rover # Enable on boot sudo systemctl disable rover # Disable on boot # View service logs sudo journalctl -u rover -f # Follow logs in real-time sudo journalctl -u rover --since today # Today's logs ``` -------------------------------- ### Install Rover Linux Service Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md Installs a Linux service for the rover, enabling it to run automatically at boot time. This involves navigating to the service directory and executing an installation script. ```shell cd ~/ros2_ws/src/ros2_rover/rover_service sudo ./install.sh ``` -------------------------------- ### Docker Deployment for ROS 2 Rover Source: https://context7.com/mgonzs13/ros2_rover/llms.txt This section details how to containerize the ROS 2 rover project for portable testing and development using Docker. It includes commands for building the Docker image, running containers with display forwarding for GUI applications, and running containers with hardware access for real robot interaction. A Docker Compose example is also provided for orchestrating multi-container applications. ```bash # Build Docker image cd ~/ros2_rover docker build -t rover . # Run container with display forwarding (for GUI) xhost +local:docker docker run -it --rm \ --env="DISPLAY=$DISPLAY" \ --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \ --network=host \ rover # Inside container - launch simulation ros2 launch rover_gazebo moon.launch.py # Run container with hardware access (for real robot) docker run -it --rm \ --device=/dev/ttyUSB0 \ --device=/dev/ttyACM0 \ --device=/dev/input/js0 \ --privileged \ rover # Inside container - launch hardware ros2 launch rover_bringup rover.launch.py # Docker Compose example (docker-compose.yml) # version: '3' # services: # rover: # build: . # privileged: true # devices: # - /dev/ttyUSB0 # - /dev/input/js0 # environment: # - DISPLAY=$DISPLAY # volumes: # - /tmp/.X11-unix:/tmp/.X11-unix:rw # command: ros2 launch rover_bringup rover.launch.py ``` -------------------------------- ### Launch Gazebo Forest Simulation Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md Launches the Gazebo simulation environment configured for a forest. This command starts a simulation with a terrestrial forest environment. ```shell ros2 launch rover_gazebo forest.launch.py ``` -------------------------------- ### Launch Gazebo Mars Simulation Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md Launches the Gazebo simulation environment configured for Mars. This command starts a simulation with Martian terrain and lighting conditions. ```shell ros2 launch rover_gazebo mars.launch.py ``` -------------------------------- ### Launch Gazebo Moon Simulation Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md Launches the Gazebo simulation environment configured for the Moon. This command starts a simulation with lunar terrain and lighting conditions. ```shell ros2 launch rover_gazebo moon.launch.py ``` -------------------------------- ### CMake Build Configuration and Package Discovery Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_gazebo/CMakeLists.txt Sets the minimum CMake version, project name, C/C++ standards, compiler warnings, and discovers required ROS 2 packages. This is a standard setup for ROS 2 packages using ament. ```cmake cmake_minimum_required(VERSION 3.5) project(rover_gazebo) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(nav_msgs REQUIRED) find_package(sensor_msgs REQUIRED) find_package(geometry_msgs REQUIRED) find_package(tf2_ros REQUIRED) find_package(rover_msgs REQUIRED) find_package(std_msgs REQUIRED) find_package(trajectory_msgs REQUIRED) ``` -------------------------------- ### Clear Navigation2 Costmap Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Calls the service to clear the global costmap of the Navigation2 stack. This is useful when the rover gets stuck or encounters planning issues. ```bash # Clear costmap (useful when stuck) ros2 service call /global_costmap/clear_entirely_global_costmap \ nav2_msgs/srv/ClearEntireCostmap ``` -------------------------------- ### Check Rover Service Status Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md Checks the status of the installed rover Linux service. This command is useful for verifying if the service is running correctly or for debugging issues. ```shell sudo service rover status ``` -------------------------------- ### Publish Rover Velocity Commands (Python) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Publishes geometry_msgs/Twist messages to the /cmd_vel topic to control the rover's linear and angular velocities. This node requires rclpy and ROS 2 environment setup. It takes speed and angular_speed as inputs and publishes Twist messages. ```python import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist class RoverController(Node): def __init__(self): super().__init__('rover_controller') self.cmd_vel_pub = self.create_publisher(Twist, '/cmd_vel', 10) def move_forward(self, speed=0.5): """Move rover forward at specified speed (m/s)""" msg = Twist() msg.linear.x = speed msg.angular.z = 0.0 self.cmd_vel_pub.publish(msg) def turn_left(self, angular_speed=0.3): """Turn rover left while moving forward""" msg = Twist() msg.linear.x = 0.3 msg.angular.z = angular_speed self.cmd_vel_pub.publish(msg) # Usage rclpy.init() controller = RoverController() controller.move_forward(0.5) # Move forward at 0.5 m/s controller.turn_left(0.3) # Turn left at 0.3 rad/s ``` -------------------------------- ### Advanced Custom Gazebo Simulation Configuration Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches a custom Gazebo simulation with detailed configuration options. This includes specifying the world file, initial pose, GUI settings, Rviz, physics pausing, navigation parameters, and odometry. ```bash # Advanced: Custom Gazebo configuration ros2 launch rover_gazebo gazebo.launch.py \ world:=/path/to/custom.world \ initial_pose_x:=0.0 \ initial_pose_y:=0.0 \ initial_pose_z:=0.22 \ initial_pose_yaw:=0.0 \ launch_gui:=True \ launch_rviz:=True \ pause_gz:=False \ nav2_planner:=SmacHybrid \ nav2_controller:=RPP \ launch_odometry:=True ``` -------------------------------- ### Testing Configuration Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_motor_controller_cpp/CMakeLists.txt Sets up testing by finding the 'ament_cmake_clang_format' package and configuring clang-format for code style checks if testing is enabled. ```cmake # TEST if(BUILD_TESTING) find_package(ament_cmake_clang_format REQUIRED) ament_clang_format(CONFIG_FILE .clang-format) endif() ament_package() ``` -------------------------------- ### Launch Navigation2 Stack Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the Navigation2 stack for autonomous navigation. Allows configuration of the planner (e.g., 'SmacHybrid'), controller (e.g., 'RPP'), and the command velocity topic. ```bash # Launch navigation stack ros2 launch rover_navigation bringup.launch.py \ use_sim_time:=false \ planner:=SmacHybrid \ controller:=RPP \ cmd_vel_topic:=cmd_vel ``` -------------------------------- ### Launch Rover Simulation Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md Launches the rover simulation using ROS 2. This command initiates the rover's main launch file, bringing up all necessary nodes and configurations for operation. ```shell ros2 launch rover_bringup rover.launch.py ``` -------------------------------- ### Launch RTABMap SLAM Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the RTAB-Map SLAM system for mapping and localization. It supports RGBD odometry, loop closure detection, 3D occupancy grid generation, and GTSAM optimization. Map data and grid maps are published for navigation. ```bash # Launch RTABMap SLAM ros2 launch rover_localization rtabmap.launch.py \ use_sim_time:=False \ launch_rtabmapviz:=True # Launch RGBD odometry only (without mapping) ros2 launch rover_localization rgbd_odometry.launch.py # Save map ros2 service call /rtabmap/trigger_new_map std_srvs/srv/Empty # Reset map ros2 service call /rtabmap/reset std_srvs/srv/Empty ``` -------------------------------- ### Build Docker Image for ros2_rover Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md These shell commands are used to build a Docker image for the ros2_rover project. The first command builds the image, and the second command runs a container from the created image. ```shell docker build -t rover . docker run -it --rm rover ``` -------------------------------- ### Send Navigation Goal to NavigateToPose Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Sends a navigation goal to the '/navigate_to_pose' action server using the 'nav2_msgs/action/NavigateToPose' type. Specifies the target pose with header frame ID and position/orientation. ```bash # Send navigation goal via action ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose \ "{pose: {header: {frame_id: 'map'}}, \ pose: {position: {x: 5.0, y: 2.0, z: 0.0}, \ orientation: {w: 1.0}}}" ``` -------------------------------- ### Send Navigation Goal to NavigateThroughPoses Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Sends a navigation goal to the '/navigate_through_poses' action server, enabling the rover to navigate through a series of waypoints. Requires a list of poses. ```bash # Navigate through multiple waypoints ros2 action send_goal /navigate_through_poses nav2_msgs/action/NavigateThroughPoses \ "{poses: [{header: {frame_id: 'map'}, pose: {position: {x: 2.0, y: 1.0}}}, \ {header: {frame_id: 'map'}, pose: {position: {x: 5.0, y: 3.0}}}]}" ``` -------------------------------- ### Launch Rover with Python Motor Controller Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the rover system using a Python-based motor controller. Allows configuration of hardware distances, the serial port for the motor controller, and the baud rate. ```bash # Alternative: Use Python motor controller ros2 launch rover_motor_controller motor_controller.launch.py \ hardware_distances:="[23.0, 25.5, 28.5, 26.0]" \ motor_controller_device:="/dev/ttyUSB0" \ baud_rate:=115200 ``` -------------------------------- ### Launch Gazebo Rover in Forest Environment Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the rover simulation in the Forest environment. This command provides a basic launch for the Forest simulation. ```bash # Launch Forest environment ros2 launch rover_gazebo forest.launch.py ``` -------------------------------- ### Launch Gazebo Rover in Moon Environment Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the rover simulation in the Moon environment with navigation enabled. Allows configuration of the navigation planner (e.g., 'SmacHybrid') and controller (e.g., 'RPP'), and Rviz visualization. ```bash # Launch Moon environment with navigation ros2 launch rover_gazebo moon.launch.py \ nav2_planner:=SmacHybrid \ nav2_controller:=RPP \ launch_rviz:=True ``` -------------------------------- ### CMake Build Configuration and Dependencies Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_motor_controller_cpp/CMakeLists.txt Sets up the CMake build system for the ROS 2 project, specifying C++ standards, enabling compiler warnings for GCC and Clang, and finding necessary ROS 2 packages. It includes dependencies like rclcpp, rover_msgs, and geometry_msgs. ```cmake cmake_minimum_required(VERSION 3.5) project(rover_motor_controller_cpp) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() # find dependencies find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(rover_msgs REQUIRED) find_package(geometry_msgs REQUIRED) INCLUDE_DIRECTORIES( include ) ``` -------------------------------- ### EKF Localization Configuration Overview Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Provides an overview of the EKF configuration file ('rover_localization/config/ekf.yaml'). It details the input sensor data (visual odometry, IMU) and the output estimates (odometry, acceleration, TF transforms). ```yaml # Configuration (rover_localization/config/ekf.yaml) # Inputs: # - odom_rgbd: Visual odometry from depth camera # - imu: IMU data (orientation and angular velocity) # Outputs: # - /odom: Filtered odometry estimate # - /accel: Filtered acceleration estimate # - TF: odom -> base_link transform # The EKF fuses: # - Position (x, y, z) from visual odometry # - Orientation (yaw) from visual odometry # - Orientation (roll, pitch) from IMU ``` -------------------------------- ### Launch Extended Kalman Filter (EKF) Localization Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the EKF localization node for sensor fusion, combining visual odometry and IMU data for pose estimation. Requires setting the 'use_sim_time' parameter. ```bash # Launch EKF localization ros2 launch rover_localization ekf.launch.py use_sim_time:=False ``` -------------------------------- ### Launch Joystick Teleoperation Separately Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the joystick teleoperation node, allowing control of the rover using a joystick. Supports configuring the joystick type (e.g., 'ps3') and the device path. ```bash # Launch joystick teleop separately ros2 launch rover_teleop joy_teleop.launch.py \ joy_config:=ps3 \ joy_dev:=/dev/input/js0 ``` -------------------------------- ### Launch URG Laser Scanner Separately Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the URG (Hokuyo) laser scanner node. Requires specifying the sensor interface device (e.g., '/dev/ttyACM0'). ```bash # Launch URG laser scanner separately ros2 launch rover_bringup urg_node.launch.py \ sensor_interface:=/dev/ttyACM0 ``` -------------------------------- ### Launch Complete Physical Rover System Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the complete rover hardware, including motor control, sensors, and teleoperation. This includes the URG laser scanner, PS3 joystick teleoperation, and the C++ motor controller by default. ```bash # Launch complete rover system ros2 launch rover_bringup rover.launch.py # This starts: # 1. URG (Hokuyo) laser scanner # 2. PS3 joystick teleop (joy_linux + teleop_twist_joy) # 3. Motor controller (C++ version by default) ``` -------------------------------- ### Keyboard Teleoperation Node Implementation Details Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Details the key bindings and velocity factors used in the 'teleop_keyboard_node.py' script. It maps keys to linear and angular velocity adjustments and specifies the publish rate. ```python # Implementation details from teleop_keyboard_node.py move_bindings = { "a": (0, 0, 0, 1), # Turn left "w": (1, 0, 0, 0), # Forward "s": (0, 0, 0, 0), # Stop "d": (0, 0, 0, -1), # Turn right "x": (-1, 0, 0, 0), # Backward } x_factor = 0.1 # Linear velocity increment th_factor = 0.1 # Angular velocity increment # Each keypress modifies velocity by the factor # Velocity accumulates until stop command ('s') is sent # Publishes at 10 Hz to /cmd_vel topic ``` -------------------------------- ### Define and Link Executable: odometry_node Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_gazebo/CMakeLists.txt Defines the 'odometry_node' executable, specifying its source file and linking it with ROS 2 navigation, sensor, geometry, and tf2 packages. This node likely calculates and publishes odometry information. ```cmake add_executable(odometry_node src/odometry_node.cpp) ament_target_dependencies(odometry_node rclcpp nav_msgs sensor_msgs geometry_msgs tf2_ros ) ``` -------------------------------- ### Launch C++ Motor Controller Separately Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches only the C++ motor controller node. This is useful for testing or integrating the motor control component independently. ```bash # Launch only C++ motor controller ros2 launch rover_motor_controller_cpp motor_controller.launch.py ``` -------------------------------- ### Launch Keyboard Teleoperation Node Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the keyboard teleoperation node for controlling the rover using keyboard input. This node publishes velocity commands to the '/cmd_vel' topic. ```bash # Launch keyboard teleop ros2 run rover_teleop teleop_keyboard_node ``` -------------------------------- ### Launch Gazebo Rover in Mars Environment Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Launches the rover simulation in the Mars environment. This command provides a basic launch for the Mars simulation. ```bash # Launch Mars environment ros2 launch rover_gazebo mars.launch.py ``` -------------------------------- ### Project Citation (BibTeX) Source: https://github.com/mgonzs13/ros2_rover/blob/humble/README.md BibTeX entry for the research paper 'Comparison of Concentric Surface Planetary Explorations Using an Ackerman Rover in a Lunar Simulation' which utilized version v0.7 of the ros2_rover project. ```bibtex @INPROCEEDINGS{10685849, author={González-Santamarta, Miguel Á. and Rodríguez-Lera, Francisco J.}, booktitle={2024 International Conference on Space Robotics (iSpaRo)}, title={Comparison of Concentric Surface Planetary Explorations Using an Ackerman Rover in a Lunar Simulation}, year={2024}, volume={}, number={}, pages={239-244}, keywords={Spirals;Navigation;Moon;Cameras;Sensors;Visual odometry;Testing}, doi={10.1109/iSpaRo60631.2024.10685849} } ``` -------------------------------- ### Control LX16A Motors and Servos (C++) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Offers a C++ implementation for controlling LX16A servo/motors, suitable for real-time applications. It requires the 'lx16a' library and a serial connection. Accepts motor speed and servo position vectors, with options for direct servo manipulation and status reading. ```cpp #include "lx16a/motor_controller.hpp" #include // Initialize controller lx16a::MotorController controller("/dev/ttyUSB0", 115200); // Set drive motor speeds std::vector drive_speeds = {500, 500, 500, -500, -500, -500}; controller.send_motor_duty(drive_speeds); // Set corner servo positions std::vector corner_positions = {450, 550, 550, 450}; controller.corner_to_position(corner_positions); // Emergency stop controller.kill_motors(); // Direct servo control for advanced usage lx16a::LX16A servo("/dev/ttyUSB0", 115200); servo.move(servo_id=1, position=500, time=1000); // Move to position in 1000ms servo.set_motor_mode(servo_id=1, speed=500); // Set continuous rotation servo.set_servo_mode(servo_id=1); // Switch to position control int position = servo.get_position(servo_id=1); // Read current position uint8_t temp = servo.get_temperature(servo_id=1); // Read temperature ``` -------------------------------- ### Monitor Localization Quality Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Monitors the localization quality of the rover by echoing the diagnostics topic. This helps in assessing the performance of odometry and IMU data. ```bash ros2 topic echo /diagnostics ``` -------------------------------- ### Define and Link Executable: ground_truth_remapper_node Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_gazebo/CMakeLists.txt Defines the 'ground_truth_remapper_node' executable, specifying its source file and linking it with ROS 2 navigation and geometry packages. This node may remap or process ground truth data. ```cmake add_executable(ground_truth_remapper_node src/ground_truth_remapper_node.cpp) ament_target_dependencies(ground_truth_remapper_node rclcpp nav_msgs geometry_msgs ) ``` -------------------------------- ### Control LX16A Motors and Servos (Python) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Provides Python control for LX16A servo/motors used for drive motors and steering servos. Requires the 'rover_motor_controller' library and a serial connection to the motors (e.g., /dev/ttyUSB0). It accepts lists of motor speeds and servo positions, and can read motor status. ```python from rover_motor_controller.lx16a import MotorController # Initialize motor controller controller = MotorController(device="/dev/ttyUSB0", baud_rate=115200) # Set drive motor speeds: [-1000 to 1000] # Order: [left_front, left_middle, left_back, right_front, right_middle, right_back] drive_speeds = [500, 500, 500, -500, -500, -500] # Forward motion controller.send_motor_duty(drive_speeds) # Set corner servo positions: [250 to 750] (500 = center/straight) # Order: [left_front, right_front, left_back, right_back] corner_positions = [450, 550, 550, 450] # Left turn controller.corner_to_position(corner_positions) # Emergency stop - aligns corners and stops all motors controller.kill_motors() # Read motor status voltage = controller.servo.vin_read(motor_id=1) # Returns voltage in mV temp = controller.servo.temp_read(motor_id=1) # Returns temperature in °C is_on = controller.servo.is_motor_on(motor_id=1) # Returns True if motor is on ``` -------------------------------- ### Odometry Calculation from Wheel Encoders (C++) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt This C++ code calculates the rover's pose (position and orientation) from wheel joint states using differential drive kinematics. It subscribes to '/joint_states', computes linear and angular velocities, and publishes odometry messages to '/wheel_odom'. Dependencies include ROS 2, nav_msgs, and sensor_msgs. It assumes a fixed update rate and uses exact integration for curved motion and Runge-Kutta for straight motion. ```cpp #include "rover_gazebo/odometry_node.hpp" #include #include #include class OdometryNode : public rclcpp::Node { private: double wheel_radius_; // Wheel radius (m) double wheel_separation_; // Distance between wheels (m) double x_, y_, heading_; // Pose estimate double linear_, angular_; // Velocity estimate public: OdometryNode() : Node("odometry_node") { this->declare_parameter("wheel_radius", 0.06); this->declare_parameter("wheel_separation", 0.5458); wheel_radius_ = this->get_parameter("wheel_radius").as_double(); wheel_separation_ = this->get_parameter("wheel_separation").as_double(); x_ = y_ = heading_ = 0.0; // Subscribe to joint states from Gazebo auto sub = this->create_subscription( "/joint_states", 10, std::bind(&OdometryNode::callback, this, std::placeholders::_1)); // Publish odometry odom_pub_ = this->create_publisher("/wheel_odom", 10); } void callback(const sensor_msgs::msg::JointState::SharedPtr msg) { // Extract left and right wheel velocities double left_vel = (msg->velocity[0] + msg->velocity[1] + msg->velocity[2]) / 3.0; double right_vel = (msg->velocity[3] + msg->velocity[4] + msg->velocity[5]) / 3.0; // Convert to linear and angular velocities linear_ = (left_vel + right_vel) / 2.0 * wheel_radius_; angular_ = (right_vel - left_vel) / wheel_separation_ * wheel_radius_; // Integrate to get pose (using exact integration for curved motion) double dt = 0.05; // Assuming 20 Hz update rate if (std::abs(angular_) > 1e-6) { // Curved motion - exact integration double dx = linear_ / angular_ * (sin(angular_ * dt)); double dy = linear_ / angular_ * (1 - cos(angular_ * dt)); x_ += dx * cos(heading_) - dy * sin(heading_); y_ += dx * sin(heading_) + dy * cos(heading_); heading_ += angular_ * dt; } else { // Straight motion - Runge-Kutta 2nd order x_ += linear_ * cos(heading_) * dt; y_ += linear_ * sin(heading_) * dt; } // Publish odometry message auto odom = nav_msgs::msg::Odometry(); odom.header.stamp = this->now(); odom.header.frame_id = "odom"; odom.child_frame_id = "base_link"; odom.pose.pose.position.x = x_; odom.pose.pose.position.y = y_; odom.twist.twist.linear.x = linear_; odom.twist.twist.angular.z = angular_; odom_pub_->publish(odom); } private: rclcpp::Publisher::SharedPtr odom_pub_; }; // Usage: launched automatically in Gazebo simulations // ros2 launch rover_gazebo odometry.launch.py wheel_radius:=0.06 wheel_separation:=0.5458 ``` -------------------------------- ### Custom Motor Command Messages (CLI) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Publishes motor commands using a custom `MotorsCommand` message type via the command line. This provides a quick way to send motor commands without writing a script. ```bash ros2 topic pub /motors_command rover_msgs/msg/MotorsCommand \ "{drive_motor: [500, 500, 500, -500, -500, -500], \ corner_motor: [500, 500, 500, 500]}" ``` -------------------------------- ### Controller Node Executable Definition and Dependencies Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_motor_controller_cpp/CMakeLists.txt Defines the 'controller_node' executable, listing its source files and linking necessary libraries such as pthread and boost_system. It declares its ROS 2 dependencies, including rclcpp and rover_msgs. ```cmake ### CONTROLLER NODE set(CONTROLLER_NODE_SOURCES src/controller_node_main.cpp src/lx16a/serial.cpp src/lx16a/lx16a.cpp src/lx16a/motor_controller.cpp src/motor_controller/controller_node.cpp ) add_executable(controller_node ${CONTROLLER_NODE_SOURCES}) target_link_libraries(controller_node pthread boost_system) ament_target_dependencies(controller_node rclcpp rover_msgs) ``` -------------------------------- ### Define and Link Executable: motors_command_parser_node Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_gazebo/CMakeLists.txt Defines the 'motors_command_parser_node' executable, specifying its source file and linking it with ROS 2 and custom rover message packages. This node is responsible for parsing motor commands. ```cmake INCLUDE_DIRECTORIES( include ) add_executable(motors_command_parser_node src/motors_command_parser_node.cpp) target_link_libraries(motors_command_parser_node) ament_target_dependencies(motors_command_parser_node rclcpp rover_msgs std_msgs trajectory_msgs ) ``` -------------------------------- ### Custom Motor Command Messages (Python) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Publishes motor commands using a custom `MotorsCommand` message type. This allows for direct control of individual drive motors and corner servos, with specified speed and position ranges. ```python import rclpy from rclpy.node import Node from rover_msgs.msg import MotorsCommand class DirectMotorControl(Node): def __init__(self): super().__init__('direct_motor_control') self.pub = self.create_publisher(MotorsCommand, '/motors_command', 10) def send_command(self): msg = MotorsCommand() # Drive motors: speed range [-1000 to 1000] # Order: [left_front, left_middle, left_back, right_front, right_middle, right_back] msg.drive_motor = [300, 300, 300, -300, -300, -300] # Forward at 30% speed # Corner servos: position range [250 to 750] (500 = straight) # Order: [left_front, right_front, left_back, right_back] msg.corner_motor = [400, 600, 600, 400] # Turn right self.pub.publish(msg) self.get_logger().info('Published motor command') # Usage rclpy.init() node = DirectMotorControl() node.send_command() ``` -------------------------------- ### Velocity Parser Node Executable Definition and Dependencies Source: https://github.com/mgonzs13/ros2_rover/blob/humble/rover_motor_controller_cpp/CMakeLists.txt Defines the 'vel_parser_node' executable, specifying its source files. It declares its ROS 2 dependencies, including rclcpp, rover_msgs, and geometry_msgs. ```cmake ### VEL PARSER NODE set(VEL_PARSER_NODE_SOURCES src/vel_parser_node_main.cpp src/motor_controller/vel_parser_node.cpp ) add_executable(vel_parser_node ${VEL_PARSER_NODE_SOURCES}) target_link_libraries(vel_parser_node) ament_target_dependencies(vel_parser_node rclcpp rover_msgs geometry_msgs) ``` -------------------------------- ### Parse Velocity Commands to Rover Movements (Python) Source: https://context7.com/mgonzs13/ros2_rover/llms.txt A ROS 2 node (VelParserNode) that converts incoming Twist velocity commands into specific motor and servo commands for the rover, utilizing Ackermann steering geometry. This node is configurable via ROS parameters for hardware distances, servo limits, speed factors, and velocity limits. ```python from rover_motor_controller.motor_controller import VelParserNode import rclpy # Launch with custom parameters rclpy.init() node = VelParserNode() # Node parameters (configured via launch file or command line) # hardware_distances: [23.0, 25.5, 28.5, 26.0] # d1, d2, d3, d4 in cm # enc_min: 250 # Servo encoder minimum # enc_max: 750 # Servo encoder maximum # speed_factor: 10 # Speed multiplier # linear_limit: 1.0 # Max linear velocity (m/s) # angular_limit: 1.0 # Max angular velocity (rad/s) ``` -------------------------------- ### Rover Motor Command Node Functionality Source: https://context7.com/mgonzs13/ros2_rover/llms.txt This ROS2 node subscribes to the '/cmd_vel' topic and publishes motor commands to '/motors_command'. It calculates turning radius, individual wheel speeds for Ackermann steering, and servo angles based on input twist messages. The output includes 6 drive speeds and 4 corner positions. ```python rclpy.spin(node) ``` -------------------------------- ### Python Ackermann Steering Calculations Source: https://context7.com/mgonzs13/ros2_rover/llms.txt Calculates individual wheel speeds and steering angles for Ackermann geometry. The class `AckermannSteering` requires hardware distances in cm and takes drive speed and turning radius as input. It returns a list of wheel speeds or servo angles in degrees. ```python from typing import List import math class AckermannSteering: def __init__(self, hardware_distances: List[float]): """ hardware_distances: [d1, d2, d3, d4] in cm d1: distance from front axis to center d2: distance from center to back axis d3: half of front/back track width d4: half of middle track width """ self.d1, self.d2, self.d3, self.d4 = hardware_distances def calculate_velocity(self, v: float, r: float) -> List[float]: """ Calculate individual wheel speeds. Args: v: Drive speed [-100, 100] r: Turning radius [-100, 100] Returns: [left_front, left_middle, left_back, right_front, right_middle, right_back] """ if abs(r) < 0.001: # Straight line return [v, v, v, v, v, v] # Map radius to actual turning radius (55-255 cm) radius_cm = 55 + (255 - 55) * (100 - abs(r)) / 100 radius_cm *= 1 if r > 0 else -1 # Calculate speeds based on distance from turning center left_front = v * (1 - self.d3 / radius_cm) left_middle = v * (1 - self.d4 / radius_cm) left_back = v * (1 - self.d3 / radius_cm) right_front = v * (1 + self.d3 / radius_cm) right_middle = v * (1 + self.d4 / radius_cm) right_back = v * (1 + self.d3 / radius_cm) # Normalize to keep max speed at commanded value speeds = [left_front, left_middle, left_back, right_front, right_middle, right_back] max_speed = max(abs(s) for s in speeds) if max_speed > 0: factor = abs(v) / max_speed speeds = [s * factor for s in speeds] return speeds def calculate_target_deg(self, radius: float) -> List[float]: """ Calculate corner servo angles for Ackermann geometry. Args: radius: Turning radius [-100, 100] Returns: [left_front, right_front, left_back, right_back] angles in degrees """ if abs(radius) < 0.001: return [0.0, 0.0, 0.0, 0.0] # Map to actual radius radius_cm = 55 + (255 - 55) * (100 - abs(radius)) / 100 side = 1 if radius > 0 else -1 # Ackermann geometry calculations left_front = math.degrees(math.atan(self.d1 / (radius_cm - self.d3 * side))) right_front = math.degrees(math.atan(self.d1 / (radius_cm + self.d3 * side))) left_back = math.degrees(math.atan(-self.d2 / (radius_cm - self.d3 * side))) right_back = math.degrees(math.atan(-self.d2 / (radius_cm + self.d3 * side))) return [left_front * side, right_front * side, left_back * side, right_back * side] # Usage example steering = AckermannSteering([23.0, 25.5, 28.5, 26.0]) wheel_speeds = steering.calculate_velocity(v=50.0, r=30.0) # 50% speed, 30% turn servo_angles = steering.calculate_target_deg(radius=30.0) # 30% turn radius print(f"Wheel speeds: {wheel_speeds}") print(f"Servo angles: {servo_angles} degrees") ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.