TinyRobotics
Loading...
Searching...
No Matches
MicroROS.h
1#pragma once
2#include <math.h>
3#include <micro_ros_arduino.h>
4#include <nav_msgs/msg/odometry.h>
5#include <rcl/rcl.h>
6#include <rclc/executor.h>
7#include <rclc/rclc.h>
8#include <std_msgs/msg/int32.h>
9#include <geometry_msgs/msg/point32.h>
10#include <geometry_msgs/msg/twist.h>
11
12#include "TinyRobotics/communication/MessageHandler.h"
13#include "TinyRobotics/maps/IMap.h"
14#include "TinyRobotics/utils/AllocatorPSRAM.h"
15#include "TinyRobotics/utils/LoggerClass.h"
16
17namespace tinyrobotics {
18
19/**
20 * @class MicroROS
21 * @ingroup communication
22 * @brief micro-ROS interface for vehicle control and odometry on
23 * Arduino/ESP32.
24 *
25 * @note Install the micro_ros_arduino library
26 *
27 * This class sets up a micro-ROS node e.g. for an Ackermann steering robot,
28 * subscribing to velocity commands (geometry_msgs/Twist on /cmd_vel) and
29 * publishing odometry (nav_msgs/Odometry on /odom).
30 *
31 * Features:
32 * - Receives velocity commands e.g. to converts them to Ackermann steering
33 * and speed.
34 * - Publishes odometry
35 * - Publishes occupancy grid maps from IMap interface
36 * received.
37 * - Uses micro-ROS Arduino library for communication.
38 * - User provides a callback for handling incoming Twist messages.
39 *
40 * Usage:
41 * 1. Call setCallback() to provide a function for handling incoming /cmd_vel
42 * messages.
43 * 2. Call begin() to initialize micro-ROS and set up publishers/subscribers.
44 * 3. Periodically call sendOdometry() in your main loop to process messages
45 * and publish odometry.
46 *
47 * @note optional functionality: include "TinyRobotics/communication/MicroROS.h"
48 * @author Phil Schatzmann
49 * @date 2026-03-30
50 */
51
52class MicroROS : public MessageHandler {
53 public:
54 MicroROS(const char* node_name = "tinyrobotics_node") {
55 ros_node_name = node_name;
56 }
57
58 void setCallback(void (*cb)(const void* msgin)) { cmd_callback = cb; }
59
60 // void setTransport(Print&out) {
61 // set_microros_serial_transports(Serial);
62 // is_tranport_defined = true;
63 // }
64
65#ifdef ESP32
66 void setTransport(char* ssid, char* pwd, char* ip, int port = 8888) {
69 }
70#endif
71
72 bool begin() {
73 if (cmd_callback == nullptr) {
74 TRLogger.error("MicroROSS: Callback function not set!");
75 return false;
76 }
77
78 if (!is_tranport_defined) set_microros_transports();
79
80 allocator = rcl_get_default_allocator();
81 rclc_support_init(&support, 0, NULL, &allocator);
82
83 rclc_node_init_default(&node, ros_node_name, "", &support);
84
85 // Subscriber: /cmd_vel
86 rclc_subscription_init_default(
87 &sub_cmd, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(geometry_msgs, msg, Twist),
88 "/cmd_vel");
89
90 // Publisher: /odom
91 rclc_publisher_init_default(
92 &publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, Odometry),
93 "/odom");
94
95 // Executor
96 rclc_executor_init(&executor, &support.context, 1, &allocator);
97 rclc_executor_add_subscription(&executor, &sub_cmd, &cmd_msg, cmd_callback,
98 ON_NEW_DATA);
99
100 return true;
101 }
102
103 /// Publish odometry message
104 bool sendOdometry(const nav_msgs__msg__Odometry& msg) {
105 auto rc = rcl_publish(&publisher, &msg, NULL);
106 return rc == RCL_RET_OK;
107 }
108
109 /// Publish odometry message
110 bool sendOdometry(const MotionState3D state) {
111 nav_msgs__msg__Odometry msg;
112 // Zero/init all fields
113 memset(&msg, 0, sizeof(msg));
114
115 // Position
116 msg.pose.pose.position.x = state.getPosition().x;
117 msg.pose.pose.position.y = state.getPosition().y;
118 msg.pose.pose.position.z = state.getPosition().z;
119
120 // Orientation (convert yaw/pitch/roll to quaternion)
121 float cy = cosf(state.getOrientation().yaw * 0.5f);
122 float sy = sinf(state.getOrientation().yaw * 0.5f);
123 float cp = cosf(state.getOrientation().pitch * 0.5f);
124 float sp = sinf(state.getOrientation().pitch * 0.5f);
125 float cr = cosf(state.getOrientation().roll * 0.5f);
126 float sr = sinf(state.getOrientation().roll * 0.5f);
127 msg.pose.pose.orientation.w = cr * cp * cy + sr * sp * sy;
128 msg.pose.pose.orientation.x = sr * cp * cy - cr * sp * sy;
129 msg.pose.pose.orientation.y = cr * sp * cy + sr * cp * sy;
130 msg.pose.pose.orientation.z = cr * cp * sy - sr * sp * cy;
131
132 // Linear velocity
133 msg.twist.twist.linear.x = state.getSpeed().x;
134 msg.twist.twist.linear.y = state.getSpeed().y;
135 msg.twist.twist.linear.z = state.getSpeed().z;
136
137 // Angular velocity
138 msg.twist.twist.angular.x = state.getAngularVelocity().x;
139 msg.twist.twist.angular.y = state.getAngularVelocity().y;
140 msg.twist.twist.angular.z = state.getAngularVelocity().z;
141
142 // Optionally: set frame_id, child_frame_id, covariance, etc.
143 // strncpy(msg.header.frame_id, "odom", sizeof(msg.header.frame_id));
144 // strncpy(msg.child_frame_id, "base_link", sizeof(msg.child_frame_id));
145
146 return sendOdometry(msg);
147 }
148
149 /// Publish odometry message
150 bool sendOdometry(Coordinate<DistanceM> position, Orientation3D orientation) {
151 MotionState3D state(
152 position, orientation, Speed3D(0, 0, 0, SpeedUnit::MPS),
153 AngularVelocity3D(0, 0, 0, AngularVelocityUnit::RadPerSec));
154 return sendOdometry(state);
155 }
156
157 /// Publish Point32 (occupancy grid map message not available in micro-ROS)
158 bool sendPoint(const geometry_msgs__msg__Point32& msg) {
159 auto rc = rcl_publish(&publisher, &msg, NULL);
160 return rc == RCL_RET_OK;
161 }
162
163 bool sendCoordinate(const Coordinate<DistanceM>& coordinate) {
164 geometry_msgs__msg__Point32 msg;
165 msg.x = coordinate.x;
166 msg.y = coordinate.y;
167 msg.z = coordinate.z;
168 return sendPoint(msg);
169 }
170
171 /// Publish Point32 (occupancy grid map message not available in micro-ROS)
172 template <typename T>
173 bool sendMap(IMap<T>& entry) {
174 Coordinate<DistanceM> coordinate;
175 coordinate.x = entry.getCoordinate().x;
176 coordinate.y = entry.getCoordinate().y;
177 coordinate.z =
178 entry.getValue(); // Assuming getValue() returns occupancy value
179 return sendCoordinate(coordinate);
180 }
181
182 rclc_executor_t& getExecutor() { return executor; }
183
184 protected:
185 const char* ros_node_name = nullptr;
186 void (*cmd_callback)(const void* msgin) = nullptr;
187 bool is_tranport_defined = false;
188
189 rcl_node_t node;
190 rclc_executor_t executor;
191 rclc_support_t support;
192 rcl_allocator_t allocator;
193
194 rcl_subscription_t sub_cmd;
195 rcl_publisher_t publisher;
196 geometry_msgs__msg__Twist cmd_msg;
197
198 bool onMessage(const Message<float>& msg) override {
199 // This can be used to handle custom messages if needed
200 return false; // Not handled here
201 }
202
203 bool onMessage(const Message<MotionState3D>& msg) override {
204 sendOdometry(msg.value);
205 return true;
206 }
207
208 bool onMessage(const Message<Coordinate<float>>& msg) {
209 sendCoordinate(msg.value);
210 return true;
211 };
212
213};
214
215} // namespace tinyrobotics
A generic 3D coordinate class for robotics, navigation, and spatial calculations.
Definition: Coordinate.h:57
Abstract interface for 2D grid maps and occupancy maps in TinyRobotics.
Definition: IMap.h:79
Interface for handling messages in the TinyRobotics framework.
Definition: MessageHandler.h:18
micro-ROS interface for vehicle control and odometry on Arduino/ESP32.
Definition: MicroROS.h:52
bool sendPoint(const geometry_msgs__msg__Point32 &msg)
Publish Point32 (occupancy grid map message not available in micro-ROS)
Definition: MicroROS.h:158
bool sendOdometry(const MotionState3D state)
Publish odometry message.
Definition: MicroROS.h:110
bool sendOdometry(Coordinate< DistanceM > position, Orientation3D orientation)
Publish odometry message.
Definition: MicroROS.h:150
bool sendOdometry(const nav_msgs__msg__Odometry &msg)
Publish odometry message.
Definition: MicroROS.h:104
bool sendMap(IMap< T > &entry)
Publish Point32 (occupancy grid map message not available in micro-ROS)
Definition: MicroROS.h:173
bool onMessage(const Message< float > &msg) override
Handle an incoming message (pure virtual).
Definition: MicroROS.h:198
Represents the full 3D motion state of a robot or vehicle.
Definition: MotionState3D.h:53
Simple 3D orientation class (yaw, pitch, roll in radians)
Definition: Orientation3D.h:13
float roll
Roll angle (radians)
Definition: Orientation3D.h:22
float pitch
Pitch angle (radians)
Definition: Orientation3D.h:21
float yaw
Yaw angle (radians)
Definition: Orientation3D.h:20
Generic message structure for communication, parameterized by value type.
Definition: Message.h:72