ROS2硬件调试(小坦克)

📅 2026/8/13 8:59:12
ROS2硬件调试(小坦克)
1.1整体通信链路这是一款嘟嘟鼠小坦克内置ESP32控制器。三个流程图图1全景总图html网页 UbuntuROSESP小车图2链路AHTML网页控制小车流程图3链路B Ubuntu ROS2 DDSSDK控制小车1.2 网页程序控制小车实验前置条件连接小坦克热点运行 html的电脑必须与小坦克处于同一局域网下。请先连接小坦克自带的 WiFi 热点1打开电脑的 WiFi 列表2找到以Tank开头的热点例如Tank_xxxx3连接该热点WiFi 密码为123456784连接成功后即可运行 DDS_Tank_v4.html机器人默认web服务器地址为192.168.4.1:80。打开html文档连接成功即可遥控小坦克前进、后退、向左走、向右走、开灯、调亮度等操作了。1.3 ros2程序控制小车实验1.3.1 准备工作复制话题通信或者服务通信工作空间的任务代码终端下进入工作空间ws01_plumbing的src目录进入到cpp01_topic目录拷贝dds_sdk_cpp文件夹。1.3.2 修改自定义话题收发程序1.发布方实现功能包cpp01_topic的src目录下找到demo03_talker_stu.cpp并编辑文件输入如下内容/* 需求以某个固定频率发送文本学生信息包含学生的姓名、年龄、身高等数据。 */ // 1.包含头文件 #include rclcpp/rclcpp.hpp #include base_interfaces_demo/msg/student.hpp using namespace std::chrono_literals; using base_interfaces_demo::msg::Student; // 3.定义节点类 class MinimalPublisher : public rclcpp::Node { public: MinimalPublisher() : Node(student_publisher), count_(0) { // 3-1.创建发布方 publisher_ this-create_publisherStudent(topic_stu, 10); // 3-2.创建定时器 timer_ this-create_wall_timer(5000ms, std::bind(MinimalPublisher::timer_callback, this)); } private: void timer_callback() { // 3-3.组织消息并发布。 auto stu Student(); stu.name 张三; stu.age count_; stu.height 1.65; RCLCPP_INFO(this-get_logger(), 学生信息:name%s,age%d,height%.2f, stu.name.c_str(),stu.age,stu.height); publisher_-publish(stu); } rclcpp::TimerBase::SharedPtr timer_; rclcpp::PublisherStudent::SharedPtr publisher_; size_t count_; }; int main(int argc, char * argv[]) { // 2.初始化 ROS2 客户端 rclcpp::init(argc, argv); // 4.调用spin函数并传入节点对象指针。 rclcpp::spin(std::make_sharedMinimalPublisher()); // 5.释放资源 rclcpp::shutdown(); return 0; }跟原有源程序的区别1只修改了发送话题的时间从500ms修改为5000ms。2.订阅方实现功能包cpp01_topic的src目录下找到C文件demo04_listener_stu.cpp并编辑文件输入如下内容/* 需求订阅发布方发布的学生消息并输出到终端 控制坦克小车 */ // 1.包含头文件 #include rclcpp/rclcpp.hpp #include base_interfaces_demo/msg/student.hpp // 新增小车SDK头文件 #include dds_sdk/dds_sdk.h using std::placeholders::_1; using base_interfaces_demo::msg::Student; // 3.定义节点类 class MinimalSubscriber : public rclcpp::Node { public: MinimalSubscriber() : Node(student_subscriber) { // 3-1.创建订阅方 subscription_ this-create_subscriptionStudent(topic_stu, 10, std::bind(MinimalSubscriber::topic_callback, this, _1)); // 小车SDK初始化 tank_sdk_ std::make_uniquedds::DDSSDK(192.168.4.1, 9000); // 日志回调SDK打印信息通过RCLCPP输出 tank_sdk_-setLogCallback([this](int level, const std::string msg){ RCLCPP_INFO(this-get_logger(), [小车SDK] %s, msg.c_str()); }); // 电机方向、死区配置 tank_sdk_-setMotorDirection(-1, -1); tank_sdk_-setDeadzone(40); // 连接坦克 if (!tank_sdk_-connect()) { RCLCPP_ERROR(this-get_logger(), 坦克连接失败请检查WiFi热点); } else { RCLCPP_INFO(this-get_logger(), 坦克连接成功); } } // 析构函数退出时停车并断开 ~MinimalSubscriber() override { if (tank_sdk_) { tank_sdk_-stop(); tank_sdk_-disconnect(); RCLCPP_INFO(this-get_logger(), 已停止小车断开连接); } } private: // 3-2.处理订阅到的消息 void topic_callback(const Student msg) const { RCLCPP_INFO(this-get_logger(), 订阅的学生消息name%s,age%d,height%.2f, msg.name.c_str(),msg.age, msg.height); // 只有连接成功才控制小车 if (!tank_sdk_-isConnected()) { RCLCPP_WARN(this-get_logger(), 未连接坦克跳过控制); return; } // 规则年龄作为LED亮度(0~100)速度固定80 int8_t led static_castint8_t(std::clamp(msg.age, 0, 100)); // 左右轮速度80前进 tank_sdk_-sendSpeedCommand(80, 80, led); // 延时500ms后停车简单演示实际项目建议用定时器代替sleep std::this_thread::sleep_for(std::chrono::milliseconds(500)); tank_sdk_-stop(); } rclcpp::SubscriptionStudent::SharedPtr subscription_; // 小车SDK对象 std::unique_ptrdds::DDSSDK tank_sdk_; }; int main(int argc, char * argv[]) { // 2.初始化 ROS2 客户端 rclcpp::init(argc, argv); // 4.调用spin函数并传入节点对象指针。 rclcpp::spin(std::make_sharedMinimalSubscriber()); // 5.释放资源 rclcpp::shutdown(); return 0; }跟之前的简单的publish/subscribe收发消息程序对比1集成了小坦克的SDK。// 新增小车SDK头文件#include dds_sdk/dds_sdk.h2初始化构造函数增加小车SDK的初始化// 小车SDK初始化 tank_sdk_ std::make_uniquedds::DDSSDK(192.168.4.1, 9000); // 日志回调SDK打印信息通过RCLCPP输出 tank_sdk_-setLogCallback([this](int level, const std::string msg){ RCLCPP_INFO(this-get_logger(), [小车SDK] %s, msg.c_str()); }); // 电机方向、死区配置 tank_sdk_-setMotorDirection(-1, -1); tank_sdk_-setDeadzone(40); // 连接坦克 if (!tank_sdk_-connect()) { RCLCPP_ERROR(this-get_logger(), 坦克连接失败请检查WiFi热点); } else { RCLCPP_INFO(this-get_logger(), 坦克连接成功); }3析构函数增加小车的逆初始化// 析构函数退出时停车并断开 ~MinimalSubscriber() override { if (tank_sdk_) { tank_sdk_-stop(); tank_sdk_-disconnect(); RCLCPP_INFO(this-get_logger(), 已停止小车断开连接); } }4处理回调的过程中解析数据用来传参以UDP方式将控制命令发送给小车侧进而控制小车动作// 只有连接成功才控制小车 if (!tank_sdk_-isConnected()) { RCLCPP_WARN(this-get_logger(), 未连接坦克跳过控制); return; } // 规则年龄作为LED亮度(0~100)速度固定80 int8_t led static_castint8_t(std::clamp(msg.age, 0, 100)); // 左右轮速度80前进 tank_sdk_-sendSpeedCommand(80, 80, led); // 延时500ms后停车简单演示实际项目建议用定时器代替sleep std::this_thread::sleep_for(std::chrono::milliseconds(500)); tank_sdk_-stop();1.3.3 修改CMakeList.txt在原CMakeList.txt文件中添加2处脚本# 【新增开始】 # 引入DDS Robot SDK 子目录 add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dds_sdk_cpp ${CMAKE_BINARY_DIR}/dds_sdk_build) # 【新增结束】 #【新增开始】 #demo04_listener_stu这个节点要控制小车追加链接库 target_link_libraries(demo04_listener_stu dds_sdk) #【新增结束】1.3.4 编译运行1编译终端中进入当前工作空间编译功能包colcon buildcolcon build --packages-select cpp01_topic2执行当前工作空间下启动两个终端终端1执行发布程序终端2执行订阅程序。终端1输入如下指令. install/setup.bashros2 run cpp01_topic demo03_talker_stu终端2输入如下指令. install/setup.bashros2 run cpp01_topic demo04_listener_stu如要清理工程请使用如下命令colcon clean workspace -y#安装python3-colcon-cleansudo apt install python3-colcon-clean#查询是否安装成功apt list --installed | grep python3-colcon-clean重新编译终端中进入当前工作空间编译功能包colcon buildcolcon build --packages-select cpp01_topic运行结果如下1.3.5 tcpdump抓包(1)获取UDP通信的数据sudotcpdump-i ens33 host 192.168.4.1 -A-i ens33你的虚拟机网卡host 192.168.4.1只抓和小车之间通信-A把报文内容以文本打印到终端你发送的前进后退文字命令直接打印在屏幕(2)如果报文复杂想看websocket之类sudotcpdump-i ens33 host 192.168.4.1 -w car.pcaptcpdump 保存文件打开 Wireshark 进行分析下载WiresharkPortable64_4.6.7.paf.exe软件将car.pcap拖入The Wireshark Network Analyzer下载car.pcap文件如下图所示协议说明SDK 使用 UDP 协议与机器人通信数据包格式[0x24, 0xC1, 0x00, 0x00, 0x0A, data[10], 0x00]其中 data 数组data[1]: 左电机速度-100 ~ 100data[3]: 右电机速度-100 ~ 100data[8]: LED 亮度0 ~ 100data[9]: 运动模式固定 0x04// 左右轮速度80前进 tank_sdk_-sendSpeedCommand(80, 80, led);此时led04为啥80的速度对应十六进制不是0x50而是0xb0呢。因为tank_sdk_-setMotorDirection(-1, -1);点击方向反转传输的是-80-80-80的补码十六进制就是0xb0。