【ESP32-S3】WINDOWS+VMware+ROS2+YDLIDA X2导航测试
-
-
- 背景
- 一、核心软硬件清单(适配Windows+VMware环境)
- 二、详细实现步骤
-
- [步骤1:VMware安装并配置Ubuntu 22.04(Windows宿主机)](#步骤1:VMware安装并配置Ubuntu 22.04(Windows宿主机))
-
- [1.1 准备安装文件](#1.1 准备安装文件)
- [1.2 创建并安装Ubuntu虚拟机](#1.2 创建并安装Ubuntu虚拟机)
- [1.3 虚拟机后置配置(必做)](#1.3 虚拟机后置配置(必做))
- [步骤2:Ubuntu虚拟机中搭建ROS2 Humble环境](#步骤2:Ubuntu虚拟机中搭建ROS2 Humble环境)
-
- [2.1 安装ROS2 Humble核心包](#2.1 安装ROS2 Humble核心包)
- [2.2 创建ROS2工作空间](#2.2 创建ROS2工作空间)
- [步骤3:硬件连接与调试(重点解决VMware USB透传)](#步骤3:硬件连接与调试(重点解决VMware USB透传))
-
- [3.1 YDLIDAR X2雷达调试(核心)](#3.1 YDLIDAR X2雷达调试(核心))
-
- [3.1.1 串口透传配置](#3.1.1 串口透传配置)
- [3.1.2 串口权限配置(避免访问被拒)](#3.1.2 串口权限配置(避免访问被拒))
- [3.1.3 部署X2的ROS2驱动并测试](#3.1.3 部署X2的ROS2驱动并测试)
- [3.1.4 测试雷达是否正常发布点云](#3.1.4 测试雷达是否正常发布点云)
- [3.2 ESP32-S3与Ubuntu的通信配置](#3.2 ESP32-S3与Ubuntu的通信配置)
-
- [3.2.1 ESP32端代码修改(核心:接收WiFi指令并控制电机)](#3.2.1 ESP32端代码修改(核心:接收WiFi指令并控制电机))
- [3.2.2 Ubuntu端编写ROS2节点(发送速度指令到ESP32)](#3.2.2 Ubuntu端编写ROS2节点(发送速度指令到ESP32))
- 步骤4:ROS2导航配置(核心)
-
- [4.1 配置坐标系(TF变换)](#4.1 配置坐标系(TF变换))
- [4.2 里程计发布(可选但推荐)](#4.2 里程计发布(可选但推荐))
- [4.3 SLAM建图(生成环境地图)](#4.3 SLAM建图(生成环境地图))
- [4.4 自主导航](#4.4 自主导航)
- 步骤5:常见问题排查
- 总结
-
背景
想在Windows电脑上通过VMware安装Linux系统,为基于ESP32-S3的电机小车(已支持HTML界面控制)集成YDLIDAR X2激光雷达,并基于ROS2实现小车的自主导航功能。这只是调试,因为雷达直接连接的电脑,测试一下ros2是否和esp32-s3进行通信
一、核心软硬件清单(适配Windows+VMware环境)
| 组件 | 作用 | 关键注意事项 |
|---|---|---|
| Windows主机 | 运行VMware虚拟机 | 需开启虚拟化(BIOS中开启Intel VT-x/AMD-V) |
| VMware Workstation | 安装Ubuntu系统 | 推荐16/17版本,支持USB 3.0透传 |
| Ubuntu 22.04 LTS | 运行ROS2、雷达驱动、导航算法 | 桌面版(新手友好),分配≥4G内存、≥40G硬盘 |
| ESP32-S3开发板 | 小车电机控制+里程计数据采集 | 支持WiFi(和Ubuntu通信)/USB串口(备选) |
| YDLIDAR X2 | 激光点云采集 | USB转串口,需透传到Ubuntu虚拟机 |
| 带编码器的直流电机 | 动力+里程计 | 导航必需(无编码器无法提供准确位姿) |
| 电机驱动(DRV8833) | 驱动电机 | ESP32 GPIO控制 |
| 电源系统 | 供电 | 5V给ESP32/雷达,7-12V给电机驱动 |
二、详细实现步骤
步骤1:VMware安装并配置Ubuntu 22.04(Windows宿主机)
这一步是基础,重点解决虚拟机网络 和USB透传问题(导航依赖这两个功能)。
1.1 准备安装文件
- 下载VMware Workstation Pro(官网下载试用版/破解版,推荐17.x);
- 下载Ubuntu 22.04 LTS 桌面版镜像(官网链接,选择
ubuntu-22.04.3-desktop-amd64.iso)。
1.2 创建并安装Ubuntu虚拟机
- 打开VMware,点击「创建新的虚拟机」→ 选择「典型」→ 下一步;
- 选择「安装程序光盘映像文件」,导入下载的Ubuntu ISO文件 → 下一步;
- 设置Ubuntu的用户名/密码(记住!)→ 下一步;
- 设置虚拟机名称和保存路径(建议非C盘)→ 下一步;
- 分配硬盘容量(≥40G,推荐60G),选择「将虚拟磁盘存储为单个文件」→ 下一步;
- 点击「自定义硬件」:
- 内存:≥4G(推荐8G);
- 处理器:≥2核;
- 网络适配器:选择「桥接模式」(关键!让Ubuntu和ESP32在同一网段);
- USB控制器:选择「USB 3.1」(支持雷达的USB串口);
- 完成硬件配置,点击「开启此虚拟机」,按提示安装Ubuntu(全程默认即可,等待10-20分钟)。
1.3 虚拟机后置配置(必做)
-
安装VMware Tools(解决分辨率/共享文件夹/鼠标无缝切换):
-
点击VMware顶部「虚拟机」→ 「安装VMware Tools」;
-
Ubuntu中会出现VMware Tools光盘,复制里面的
.tar.gz文件到桌面,解压后执行:bashcd 解压后的文件夹 sudo ./vmware-install.pl -
全程按回车默认,安装完成后重启虚拟机。
-
-
更新Ubuntu系统:
bashsudo apt update && sudo apt upgrade -y
步骤2:Ubuntu虚拟机中搭建ROS2 Humble环境
ROS2 Humble是Ubuntu 22.04的官方适配版本,稳定性最佳。
2.1 安装ROS2 Humble核心包
bash
# 1. 配置系统环境(解决locale编码问题)
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8
# 2. 添加ROS2软件源
sudo apt install -y curl gnupg2 lsb-release
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
# 3. 安装ROS2 Humble桌面版+导航全套包
sudo apt update && sudo apt install -y \
ros-humble-desktop \
ros-humble-navigation2 \
ros-humble-nav2-bringup \
ros-humble-slam-toolbox \
ros-humble-tf2-tools \
python3-colcon-common-extensions \
python3-pyserial \
ros-humble-teleop-twist-keyboard
# 4. 配置环境变量(永久生效)
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc
echo "export ROS_DOMAIN_ID=0" >> ~/.bashrc # 避免多设备通信冲突
source ~/.bashrc
2.2 创建ROS2工作空间
bash
# 创建工作空间目录
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
# 初始化编译(空工作空间,生成配置文件)
colcon build --symlink-install
步骤3:硬件连接与调试(重点解决VMware USB透传)
3.1 YDLIDAR X2雷达调试(核心)
3.1.1 串口透传配置
-
将YDLIDAR X2的USB线插入Windows主机;
-
回到VMware,点击顶部「虚拟机」→「可移动设备」→ 找到「USB Serial Device」(X2的串口设备)→ 点击「连接」;
- 若未识别,检查:① Windows是否安装了CH340驱动(X2的USB转串口芯片);② VMware USB控制器是否设为3.1;
-
Ubuntu中验证串口是否识别:
bashls /dev/ttyUSB* # 正常会显示 /dev/ttyUSB0(或ttyACM0)
3.1.2 串口权限配置(避免访问被拒)
bash
# 添加当前用户到dialout组(永久生效,需重启Ubuntu)
sudo usermod -aG dialout $USER
# 临时赋予串口权限(立即生效)
sudo chmod 777 /dev/ttyUSB0
3.1.3 部署X2的ROS2驱动并测试
bash
# 克隆官方驱动到ROS2工作空间
cd ~/ros2_ws/src
git clone https://github.com/ydlidar/ydlidar_ros2_driver.git
# 编译驱动
cd ~/ros2_ws
colcon build --packages-select ydlidar_ros2_driver
# 修改雷达配置文件(匹配X2参数)
gedit ~/ros2_ws/src/ydlidar_ros2_driver/config/ydlidar_x2.yaml
将配置文件修改为以下内容(关键参数匹配X2):
yaml
ydlidar_ros2_driver_node:
ros__parameters:
port: /dev/ttyUSB0 # 雷达串口(按实际修改)
baudrate: 115200 # X2默认波特率
frame_id: laser_link # 雷达坐标系(后续导航要匹配)
scan_frequency: 7 # X2最大10Hz,7Hz更稳定
lidar_type: TYPE_X2 # 指定雷达型号
device_type: TYPE_SERIAL # 串口通信
isSingleChannel: true # X2是单通道
inverted: false # 雷达正装(反装则设为true)
angle_max: 180.0 # X2最大扫描角度180°
angle_min: -180.0
range_max: 8.0 # 最大测距8米
range_min: 0.1
use_ros_time: false # 不使用ROS时间
enable_log: false # 关闭日志(节省资源)
3.1.4 测试雷达是否正常发布点云
bash
# 启动雷达驱动
ros2 launch ydlidar_ros2_driver ydlidar_launch.py
# 新开终端,查看点云话题(正常会显示数据)
ros2 topic echo /scan
- 若报错「找不到串口」:重新透传USB设备,或检查串口名称(
/dev/ttyACM0); - 若话题无数据:检查雷达供电(X2需稳定5V),或重启雷达。
3.2 ESP32-S3与Ubuntu的通信配置
原ESP32有HTML控制,现在需要新增「接收ROS2速度指令」的逻辑。新手推荐WiFi通信(避免串口占用):
3.2.1 ESP32端代码修改(核心:接收WiFi指令并控制电机)
使用Arduino IDE编写,核心逻辑:
- ESP32连接到和Ubuntu虚拟机同网段的WiFi(比如家里的路由器);
- 建立UDP/TCP服务器,接收Ubuntu发送的「前进/后退/左转/右转/停止」指令;
- 将指令转换为电机PWM信号,控制电机驱动模块。
示例代码(关键片段,需适配你的电机引脚):
cpp
#include <WiFi.h>
#include <WiFiUdp.h>
// 1. WiFi配置(和Ubuntu同网段)
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
WiFiUDP udp;
unsigned int localPort = 8888; // 本地UDP端口
// 2. 电机引脚配置(替换为你的实际引脚)
#define LEFT_MOTOR_PWM 10
#define LEFT_MOTOR_DIR 11
#define RIGHT_MOTOR_PWM 12
#define RIGHT_MOTOR_DIR 13
// 3. 速度参数
int linear_x = 0; // 线速度(前进+,后退-,范围-100~100)
int angular_z = 0; // 角速度(左转+,右转-,范围-50~50)
void setup() {
Serial.begin(115200);
// 初始化电机引脚
pinMode(LEFT_MOTOR_PWM, OUTPUT);
pinMode(LEFT_MOTOR_DIR, OUTPUT);
pinMode(RIGHT_MOTOR_PWM, OUTPUT);
pinMode(RIGHT_MOTOR_DIR, OUTPUT);
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi连接成功,IP地址:" + WiFi.localIP().toString());
// 启动UDP
udp.begin(localPort);
}
void loop() {
// 接收UDP数据(来自Ubuntu的ROS2节点)
int packetSize = udp.parsePacket();
if (packetSize) {
char cmd[100];
int len = udp.read(cmd, sizeof(cmd));
cmd[len] = '\0';
Serial.print("收到指令:");
Serial.println(cmd);
// 解析指令(格式:linear_x,angular_z,比如"50,0"=前进,"0,30"=左转)
sscanf(cmd, "%d,%d", &linear_x, &angular_z);
controlMotor(linear_x, angular_z);
}
}
// 电机控制函数(核心:将速度指令转为PWM)
void controlMotor(int linear, int angular) {
// 计算左右轮速度(简易差速算法)
int left_speed = linear - angular;
int right_speed = linear + angular;
// 限制速度范围(0~255)
left_speed = constrain(left_speed, -100, 100);
right_speed = constrain(right_speed, -100, 100);
// 控制左轮方向和速度
if (left_speed > 0) {
digitalWrite(LEFT_MOTOR_DIR, HIGH);
analogWrite(LEFT_MOTOR_PWM, map(left_speed, 0, 100, 0, 255));
} else if (left_speed < 0) {
digitalWrite(LEFT_MOTOR_DIR, LOW);
analogWrite(LEFT_MOTOR_PWM, map(-left_speed, 0, 100, 0, 255));
} else {
analogWrite(LEFT_MOTOR_PWM, 0);
}
// 控制右轮方向和速度(逻辑同上)
if (right_speed > 0) {
digitalWrite(RIGHT_MOTOR_DIR, HIGH);
analogWrite(RIGHT_MOTOR_PWM, map(right_speed, 0, 100, 0, 255));
} else if (right_speed < 0) {
digitalWrite(RIGHT_MOTOR_DIR, LOW);
analogWrite(RIGHT_MOTOR_PWM, map(-right_speed, 0, 100, 0, 255));
} else {
analogWrite(RIGHT_MOTOR_PWM, 0);
}
}
3.2.2 Ubuntu端编写ROS2节点(发送速度指令到ESP32)
在~/ros2_ws/src下创建功能包:
bash
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python car_control --dependencies rclpy geometry_msgs
创建UDP发送节点~/ros2_ws/src/car_control/car_control/udp_publisher.py:
python
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
import socket
class CarControlNode(Node):
def __init__(self):
super().__init__('car_control_node')
# 1. 订阅ROS2标准速度话题(/cmd_vel,导航模块会发布此话题)
self.subscription = self.create_subscription(
Twist,
'/cmd_vel',
self.cmd_vel_callback,
10)
# 2. 配置UDP(ESP32的IP和端口)
self.esp32_ip = "192.168.1.100" # 替换为ESP32的实际IP
self.esp32_port = 8888
self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.get_logger().info("小车控制节点已启动,目标ESP32 IP:{}".format(self.esp32_ip))
def cmd_vel_callback(self, msg):
# 解析Twist指令(ROS2标准:linear.x=线速度(m/s),angular.z=角速度(rad/s))
# 转换为ESP32的指令范围(-100~100)
linear_x = int(msg.linear.x * 100) # 假设1m/s对应100的PWM值
angular_z = int(msg.angular.z * 50) # 假设1rad/s对应50的角速度值
# 发送指令到ESP32(格式:linear_x,angular_z)
cmd = f"{linear_x},{angular_z}"
self.udp_socket.sendto(cmd.encode(), (self.esp32_ip, self.esp32_port))
self.get_logger().info(f"发送指令:{cmd}")
def main(args=None):
rclpy.init(args=args)
node = CarControlNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
修改~/ros2_ws/src/car_control/setup.py(添加入口点):
python
# 找到entry_points部分,添加:
entry_points={
'console_scripts': [
'udp_publisher = car_control.udp_publisher:main',
],
},
编译并测试:
bash
cd ~/ros2_ws
colcon build --packages-select car_control
# 启动控制节点
ros2 run car_control udp_publisher
# 新开终端,发布速度指令(测试小车前进)
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.5, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}" --once
- 若ESP32收到指令并控制电机,说明通信正常;
- 若未收到:检查ESP32和Ubuntu的IP是否同网段,或关闭Ubuntu防火墙(
sudo ufw disable)。
步骤4:ROS2导航配置(核心)
导航的核心是「激光点云+里程计+TF变换+导航算法」,新手推荐先做SLAM建图,再做自主导航。
4.1 配置坐标系(TF变换)
创建~/ros2_ws/src/car_control/launch/tf_launch.py(定义小车的坐标系关系):
python
#!/usr/bin/env python3
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
# 定义TF变换:laser_link(雷达)→ base_link(小车本体)→ base_footprint(地面)
tf_node = Node(
package='tf2_ros',
executable='static_transform_publisher',
arguments=['0.05', '0.0', '0.1', '0.0', '0.0', '0.0', 'base_link', 'laser_link'],
# 参数说明:x y z roll pitch yaw 父坐标系 子坐标系
# 需根据你的雷达安装位置调整(比如x=0.05表示雷达在小车前方5cm)
name='laser_tf_publisher'
)
base_footprint_tf = Node(
package='tf2_ros',
executable='static_transform_publisher',
arguments=['0.0', '0.0', '0.0', '0.0', '0.0', '0.0', 'base_footprint', 'base_link'],
name='base_footprint_tf_publisher'
)
return LaunchDescription([
tf_node,
base_footprint_tf
])
4.2 里程计发布(可选但推荐)
若你的电机带编码器,在ESP32端读取编码器数据,通过UDP发送到Ubuntu,Ubuntu编写ROS2节点发布/odom话题(里程计),导航算法需要此数据做位姿估计。
4.3 SLAM建图(生成环境地图)
bash
# 启动雷达+TF变换
ros2 launch ydlidar_ros2_driver ydlidar_launch.py
ros2 launch car_control tf_launch.py
# 新开终端,启动SLAM工具(激光建图)
ros2 launch slam_toolbox online_async_launch.py
# 新开终端,手动控制小车运动(建图)
ros2 run teleop_twist_keyboard teleop_twist_keyboard
-
按键盘「w/s/a/d」控制小车移动,覆盖需要导航的区域;
-
建图完成后,保存地图:
bashros2 run nav2_map_server map_saver_cli -f ~/my_map
4.4 自主导航
bash
# 启动雷达+TF变换+里程计(若有)
ros2 launch ydlidar_ros2_driver ydlidar_launch.py
ros2 launch car_control tf_launch.py
ros2 run car_control udp_publisher
# 启动导航模块(加载保存的地图)
ros2 launch nav2_bringup navigation_launch.py map:=~/my_map.yaml
# 新开终端,启动RViz(可视化导航)
ros2 launch nav2_bringup rviz_launch.py
- 在RViz中:
- 点击「2D Pose Estimate」,在地图上标定小车当前位置;
- 点击「2D Goal Pose」,在地图上设置目标点;
- 小车会自动规划路径,避障并到达目标点。
步骤5:常见问题排查
- 雷达无数据:检查VMware USB透传、串口权限、雷达供电;
- 小车不响应ROS2指令:检查ESP32和Ubuntu的IP是否同网段、UDP端口是否匹配、电机引脚是否正确;
- 导航路径规划失败:检查TF变换是否正确、激光点云是否正常、里程计数据是否准确。
总结
- 环境核心:VMware需配置「桥接网络」和「USB 3.0透传」,确保Ubuntu能访问雷达和ESP32;
- 通信核心 :ESP32通过WiFi接收Ubuntu的UDP指令,Ubuntu订阅ROS2标准
/cmd_vel话题并转发; - 导航核心:先通过SLAM_toolbox生成地图,再用navigation2加载地图,通过RViz设置目标点实现自主导航。