Jetson nano esp32实现避障小车(ROS2技术)

一、实现功能

1、功能说明

利用以下硬件,制作一个智能小车(目前只有距离传感器硬件),实现通过ros2代码和ESP32代码,控制两个小电机的转速、转向,当接收到距离传感器返回的距离小于5厘米时,小车停止运行,并向左转向,直到检测到前方距离大于5厘米后继续向前行驶。

2.主要硬件设备

  • jetson nano,安装了ros2 humble,micro_ros,arduino,安装了一个摄像头CSI摄像头
  • ESP32 dev kit wroom-32U
  • L298N电机驱动板
  • 直流减速小电机2个
  • 超声波距离传感器

二、软硬件设计

1、硬件设计

  • Jetson Nano: 作为主控制器,运行ROS 2节点和micro_ros代理。处理摄像头图像、超声波传感器数据和其他传感器信息。执行高级算法,如计算机视觉和路径规划。通过ROS 2话题与ESP32通信。
  • ESP32: 作为微控制器,运行micro-ROS代理。直接控制L298N电机驱动板。接收Jetson Nano的指令来控制电机。
  • L298N电机驱动板: 用于驱动两个直流减速小电机。接收来自ESP32的PWM信号来控制电机速度和方向。
  • 直流减速小电机: 为小车提供动力。可以配备编码器以反馈速度和位置信息。
  • 超声波距离传感器: 用于检测小车前方的障碍物距离。

2、软件设计

  • ROS 2节点: 在Jetson Nano上运行,处理摄像头图像和超声波传感器数据。发布控制命令到ESP32。
  • micro_ros代理: 在Jetson Nano和ESP32上运行,实现两者之间的通信。
  • 电机控制: ESP32订阅来自Jetson Nano的控制话题,根据接收到的指令控制L298N电机驱动板。
  • ** 传感器数据处理:** ESP32收集超声波传感器数据,发送到Jetson Nano进行处理。
  • 安全和监控: 实现紧急停止机制,以防止碰撞或应对其他潜在危险。

3、 通信设计

  • 使用ROS 2话题进行Jetson Nano和ESP32之间的数据交换。
  • 通过无线网络实现Jetson Nano与外部系统的通信。(esp32应该连接wifi,本文暂未实现)

三、整体逻辑架构图

ROS 2 & micro_ros
CSI摄像头信号
电源管理
控制信号
电机控制
驱动
传感器信号
检测障碍物
捕捉图像
供电
供电
供电
供电
Jetson Nano
ESP32
CSI摄像头
电源
L298N电机驱动板
直流减速小电机
小车底盘
超声波距离传感器

组件说明:

  • Jetson Nano:作为主控制器,运行ROS 2和micro_ros,处理摄像头图像和传感器数据,并通过ROS 2话题与ESP32通信。
  • ESP32:作为微控制器,运行micro-ROS代理,接收Jetson Nano的指令,控制L298N电机驱动板。
  • CSI摄像头:连接到Jetson Nano,用于捕捉环境图像,进行视觉处理。(这个只是设想,暂未实现)
  • L298N电机驱动板:接收来自ESP32的控制信号,驱动两个直流减速小电机。
  • 直流减速小电机:连接到L298N电机驱动板,为小车提供动力。
  • 超声波距离传感器:连接到ESP32,用于检测前方障碍物的距离。
  • 电源:为Jetson Nano、ESP32、电机驱动板和传感器供电。
  • 小车底盘:支撑和安装所有电子组件,包括电机、电机驱动板、传感器和ESP32。

组合方式:

  • Jetson Nano通过ROS 2话题与ESP32通信,发送控制指令。
  • CSI摄像头捕捉的图像直接传输给Jetson Nano进行处理。
  • ESP32根据Jetson Nano的指令,通过PWM信号控制L298N电机驱动板上的电机。
  • 超声波距离传感器检测到的障碍物信息传输给ESP32,ESP32再将信息发送给Jetson Nano进行处理。
  • 电源管理系统为所有电子组件提供稳定的电源。

四、实现代码

1、ROS 2 Python节点代码

这个节点将订阅距离传感器的话题,发布控制命令到ESP32。

import rclpy
from rclpy.node import Node
from std_msgs.msg import Int32MultiArray
from sensor_msgs.msg import Range

class MotorControlNode(Node):
    def __init__(self):
        super().__init__('motor_control_node')
        self.publisher_ = self.create_publisher(Int32MultiArray, 'motor_control', 10)
        self.subscription = self.create_subscription(
            Range,
            'distance_sensor',
            self.distance_callback,
            10)
        self.subscription  # prevent unused variable warning

    def distance_callback(self, msg):
        if msg.range < 0.05:  # 距离小于5厘米
            self.stop_motors()
            self.get_logger().info('Obstacle detected, stopping the car')
            self.execute_evasion_maneuver()
        else:
            self.forward_motors()

    def execute_evasion_maneuver(self):
        self.turn_left()
        self.wait_for_clearance()

    def wait_for_clearance(self):
        while True:
            msg = self.subscription.get_next_message()
            if msg.range > 0.05:
                self.forward_motors()
                break

    def stop_motors(self):
        msg = Int32MultiArray()
        msg.data = [0, 0]
        self.publisher_.publish(msg)

    def forward_motors(self):
        msg = Int32MultiArray()
        msg.data = [100, 100]  # 假设100为前进的速度
        self.publisher_.publish(msg)

    def turn_left(self):
        msg = Int32MultiArray()
        msg.data = [100, -100]  # 左电机前进,右电机后退
        self.publisher_.publish(msg)

def main(args=None):
    rclpy.init(args=args)
    motor_control_node = MotorControlNode()
    rclpy.spin(motor_control_node)
    motor_control_node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

2、ESP32 micro-ROS代码

这个代码将订阅来自ROS 2的控制命令,并根据这些命令控制电机。

#include <micro_ros_arduino.h>
#include <rcl/rcl.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <std_msgs/msg/int32_multi_array.h>
#include <sensor_msgs/msg/range.h>

#include <rcl/error_handling.h>


std_msgs__msg__Int32MultiArray msg;
rcl_subscription_t subscriber;
rcl_publisher_t distance_publisher;
rclc_executor_t executor;
rclc_support_t support;
rcl_node_t node;
rcl_allocator_t allocator;

#define MOTOR_LEFT_FORWARD  5  // L298N IN1
#define MOTOR_LEFT_BACKWARD 18  // L298N IN2
#define MOTOR_RIGHT_FORWARD 23  // L298N IN3
#define MOTOR_RIGHT_BACKWARD 19  // L298N IN4
#define TRIGGER_PIN  4
#define ECHO_PIN     5
#define ENABLE_PIN   22  // L298N ENA

#define LED_PIN 13
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}

void error_loop(){
  while(1){
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    delay(100);
  }
}

void motor_control_callback(const void * msgin)
{
  const std_msgs__msg__Int32MultiArray * msg = (const std_msgs__msg__Int32MultiArray *)msgin;
  analogWrite(MOTOR_LEFT_FORWARD, msg->data.data[0]);
  analogWrite(MOTOR_LEFT_BACKWARD, msg->data.data[0]);
  analogWrite(MOTOR_RIGHT_FORWARD, msg->data.data[1]);
  analogWrite(MOTOR_RIGHT_BACKWARD, msg->data.data[1]);
  digitalWrite(ENABLE_PIN, msg->data.data[0] > 0 || msg->data.data[1] > 0 ? HIGH : LOW);
}

float measure_distance() {
  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGER_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH);
  float distance = (duration / 2) / 29.1; // 速度 = 34300 cm/s, 时间 = 距离 / (速度/2)
  return distance;
}

void setup() {
  float distance = measure_distance();
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(MOTOR_LEFT_FORWARD, OUTPUT);
  pinMode(MOTOR_LEFT_BACKWARD, OUTPUT);
  pinMode(MOTOR_RIGHT_FORWARD, OUTPUT);
  pinMode(MOTOR_RIGHT_BACKWARD, OUTPUT);
  pinMode(ENABLE_PIN, OUTPUT);

  digitalWrite(ENABLE_PIN, LOW);  // Disable motor driver initially

  allocator = rcl_get_default_allocator();

  //create init_options
  RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));

  // create node
  RCCHECK(rclc_node_init_default(&node, "esp32_node", "", &support));

  // create subscriber
  RCCHECK(rclc_subscription_init_default(
    &subscriber,
    &node,
    ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32MultiArray),
    "motor_control"));

  // create publisher
  RCCHECK(rclc_publisher_init_default(
    &distance_publisher,
    &node,
    ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, Range),
    "distance_sensor"));

  // create executor
  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
  //
  RCCHECK(rclc_executor_add_subscription(&executor, &subscriber, &msg, &motor_control_callback, ON_NEW_DATA));
}

void loop() {

  //向ros2发布距离数据
  float distance = measure_distance();
  sensor_msgs__msg__Range distance_msg;
  distance_msg.range = distance;
  distance_msg.min_range = 0.0;
  distance_msg.max_range = 2.0;
  rcl_publish(&distance_publisher, &distance_msg, NULL);
  
  //接收订阅的数据,执行回调函数
   RCCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}


  • 在这个ESP32代码中,我们定义了一个measure_distance函数来触发超声波传感器并计算距离。然后,我们创建了一个发布者来发布距离数据,并设置了执行器来处理回调。
  • 在setup函数中,我们初始化了超声波传感器的GPIO引脚,创建了一个发布者来发布距离数据,并设置了执行器来处理回调。
  • 在loop函数中,我们调用rclc_executor_spin_some来处理回调。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值