参考:官方文档
micro_ros_blog
测试环境:
- M5 Atom Lite (esp32-pico-d4 core)
- 旭日x3派(2G)+ros2 foxy
0. 写在前面
- 在micro_ros与上位机连接时,前文大部分情况下需要手动复位下位机
- 实际使用中,我们希望上电自动连接
- 尽管可以用软件控制复位,但仍较为麻烦
- 所以,我们希望能够有一种机制帮助我们实现自动连接micro_ros_agent
1. micro_ros_reconnection
1.1 官方示例
- 在micro_ros_arduino的代码中提供了一个micro-ros_reconnection.ino的示例
- 该代码利用状态机的方式判断上位机的agent是否开启,下位机是否就绪以及上位机与下位机是否连接等;
- 本文基于示例代码,通过下位机发布twist消息控制turtlesim的运动
1.2 完整代码
#include
#include
#include
#include
#include
#include
#include
#include "M5Atom.h"
#include
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){return false;}}
#define EXECUTE_EVERY_N_MS(MS, X) do { \
static volatile int64_t init = -1; \
if (init == -1) { init = uxr_millis();} \
if (uxr_millis() - init > MS) { X; init = uxr_millis();} \
} while (0)\
rclc_support_t support;
rcl_node_t node;
rcl_timer_t timer;
rclc_executor_t executor;
rcl_allocator_t allocator;
rcl_publisher_t publisher;
geometry_msgs__msg__Twist msg;
bool micro_ros_init_successful;
enum states {
WAITING_AGENT,
AGENT_AVAILABLE,
AGENT_CONNECTED,
AGENT_DISCONNECTED
} state;
void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
(void) last_call_time;
if (timer != NULL) {
rcl_publish(&publisher, &msg, NULL);
static int cnt = 0;
msg.linear.x = 0.2;
msg.angular.z = 1.0 - 0.001 * cnt;
cnt++;
}
}
bool create_entities()
{
allocator = rcl_get_default_allocator();
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
RCCHECK(rclc_node_init_default(&node, "int32_publisher_rclc", "", &support));
RCCHECK(rclc_publisher_init_best_effort(
&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(geometry_msgs, msg, Twist),
"turtle1/cmd_vel"));
const unsigned int timer_timeout = 1000;
RCCHECK(rclc_timer_init_default(
&timer,
&support,
RCL_MS_TO_NS(timer_timeout),
timer_callback));
executor = rclc_executor_get_zero_initialized_executor();
RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
RCCHECK(rclc_executor_add_timer(&executor, &timer));
return true;
}
void destroy_entities()
{
rmw_context_t * rmw_context = rcl_context_get_rmw_context(&support.context);
(void) rmw_uros_set_context_entity_destroy_session_timeout(rmw_context, 0);
rcl_publisher_fini(&publisher, &node);
rcl_timer_fini(&timer);
rclc_executor_fini(&executor);
rcl_node_fini(&node);
rclc_support_fini(&support);
}
void setup() {
M5.begin(true, false, true);
M5.dis.drawpix(0, 0x00ff00);
set_microros_transports();
state = WAITING_AGENT;
msg.linear.x = 0;
msg.linear.y = 0;
msg.linear.z = 0;
msg.angular.x = 0;
msg.angular.y = 0;
msg.angular.z = 0;
}
void loop() {
switch (state) {
case WAITING_AGENT:
EXECUTE_EVERY_N_MS(500, state = (RMW_RET_OK == rmw_uros_ping_agent(100, 1)) ? AGENT_AVAILABLE : WAITING_AGENT;);
break;
case AGENT_AVAILABLE:
state = (true == create_entities()) ? AGENT_CONNECTED : WAITING_AGENT;
if (state == WAITING_AGENT) {
destroy_entities();
};
break;
case AGENT_CONNECTED:
EXECUTE_EVERY_N_MS(200, state = (RMW_RET_OK == rmw_uros_ping_agent(100, 1)) ? AGENT_CONNECTED : AGENT_DISCONNECTED;);
if (state == AGENT_CONNECTED) {
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
}
break;
case AGENT_DISCONNECTED:
destroy_entities();
state = WAITING_AGENT;
break;
default:
break;
}
if (state == AGENT_CONNECTED) {
M5.dis.drawpix(0, 0x00ff00);
}
else if (state == WAITING_AGENT) {
M5.dis.drawpix(0, 0xfff000);
}
else if (state == AGENT_DISCONNECTED) {
M5.dis.drawpix(0, 0xff0000);
}
else if (state == AGENT_AVAILABLE) {
M5.dis.drawpix(0, 0x0000f0);
}
}
1.3 代码解析
- 关于发布"turtle1/cmd_vel"的部分代码见前文,不再赘述
- micro_ros通讯状态定义
enum states {
WAITING_AGENT,
AGENT_AVAILABLE,
AGENT_CONNECTED,
AGENT_DISCONNECTED
} state;
#define EXECUTE_EVERY_N_MS(MS, X) do { \
static volatile int64_t init = -1; \
if (init == -1) { init = uxr_millis();} \
if (uxr_millis() - init > MS) { X; init = uxr_millis();} \
} while (0)\
switch (state) {
case WAITING_AGENT:
EXECUTE_EVERY_N_MS(500, state = (RMW_RET_OK == rmw_uros_ping_agent(100, 1)) ? AGENT_AVAILABLE : WAITING_AGENT;);
break;
case AGENT_AVAILABLE:
state = (true == create_entities()) ? AGENT_CONNECTED : WAITING_AGENT;
if (state == WAITING_AGENT) {
destroy_entities();
};
break;
case AGENT_CONNECTED:
EXECUTE_EVERY_N_MS(200, state = (RMW_RET_OK == rmw_uros_ping_agent(100, 1)) ? AGENT_CONNECTED : AGENT_DISCONNECTED;);
if (state == AGENT_CONNECTED) {
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
}
break;
case AGENT_DISCONNECTED:
destroy_entities();
state = WAITING_AGENT;
break;
default:
break;
}
- 引入M5.ATOM.h,方便控制彩色LED来显示不同的micro_ros通讯状态
#include "M5Atom.h"
M5.begin(true, false, true);
M5.dis.drawpix(0, 0x00ff00);
if (state == AGENT_CONNECTED) {
M5.dis.drawpix(0, 0x00ff00);
}
else if (state == WAITING_AGENT) {
M5.dis.drawpix(0, 0xfff000);
}
else if (state == AGENT_DISCONNECTED) {
M5.dis.drawpix(0, 0xff0000);
}
else if (state == AGENT_AVAILABLE) {
M5.dis.drawpix(0, 0x0000f0);
}
- 关于LED的代码部分,可根据自己的led来改写,也可删除。
2. 测试