亚博microros小车-原生ubuntu支持系列:20 ROS Robot APP建图
依赖工程
新建工程laserscan_to_point_publisher
src/laserscan_to_point_publisher/laserscan_to_point_publisher/目录下新建文件laserscan_to_point_publish.py
#!/usr/bin/env python3import rclpy
from rclpy.node import Node
from geometry_msgs.msg import PoseStamped, TransformStamped
from nav_msgs.msg import Path
from sensor_msgs.msg import LaserScan
import tf2_ros
import mathclass laserscanToPointPublish(Node):def __init__(self):super().__init__('robot_pose_publisher')self.subscription = self.create_subscription(LaserScan,'/scan',self.laserscan_callback,10)self.sacn_point_publisher = self.create_publisher(Path,'/scan_points',10)def laserscan_callback(self, msg):
# print(msg)angle_min = msg.angle_minangle_increment = msg.angle_incrementlaserscan = msg.ranges# 获取激光雷达数据
# print(laserscan)laser_points = self.laserscan_to_points(laserscan, angle_increment, angle_increment) self.sacn_point_publisher.publish(laser_points)def laserscan_to_points(self, laserscan, angle_min, angle_increment):points = []angle = angle_minlaser_points = Path()for distance in laserscan:x = distance * math.cos(angle + 135)#获取当前激光雷达数据点的的坐标值y = distance * math.sin(angle + 135)pose = PoseStamped()pose.pose.position.x = xpose.pose.position.y = ypoints.append(pose)angle += angle_incrementlaser_points.poses = pointsreturn laser_pointsdef main(args=None):rclpy.init(args=args)robot_laser_scan_publisher = laserscanToPointPublish()rclpy.spin(robot_laser_scan_publisher)robot_pose_publisher.destroy_node()rclpy.shutdown()if __name__ == '__main__':main()
robot_pose_publisher_ros2
src/robot_pose_publisher_ros2/src/目录下新建robot_pose_publisher.cpp
/*!* \file robot_pose_publisher.cpp* \brief Publishes the robot's position in a geometry_msgs/Pose message.** Publishes the robot's position in a geometry_msgs/Pose message based on the TF* difference between /map and /base_link.** \author Milan - milan.madathiparambil@gmail.com* \date April 20 1020*/#include <chrono>
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/pose_stamped.hpp"
#include "geometry_msgs/msg/pose.hpp"
#include "geometry_msgs/msg/transform_stamped.hpp"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"using namespace std::chrono_literals;/* This example creates a subclass of Node and uses std::bind() to register a* member function as a callback from the timer. */class RobotPosePublisher : public rclcpp::Node
{
public:RobotPosePublisher() : Node("robot_pose_publisher"){tf_buffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);this->declare_parameter<std::string>("map_frame","map");this->declare_parameter<std::string>("base_frame","base_link");this->declare_parameter<bool>("is_stamped",false);this->get_parameter("map_frame", map_frame);this->get_parameter("base_frame", base_frame);this->get_parameter("is_stamped", is_stamped);if (is_stamped)publisher_stamp = this->create_publisher<geometry_msgs::msg::PoseStamped>("robot_pose", 1);elsepublisher_ = this->create_publisher<geometry_msgs::msg::Pose>("robot_pose", 1);timer_ = this->create_wall_timer(50ms, std::bind(&RobotPosePublisher::timer_callback, this));}private:void timer_callback(){geometry_msgs::msg::TransformStamped transformStamped;try{transformStamped = tf_buffer_->lookupTransform(map_frame, base_frame,this->now());}catch (tf2::TransformException &ex){return;}geometry_msgs::msg::PoseStamped pose_stamped;pose_stamped.header.frame_id = map_frame;pose_stamped.header.stamp = this->now();pose_stamped.pose.orientation.x = transformStamped.transform.rotation.x;pose_stamped.pose.orientation.y = transformStamped.transform.rotation.y;pose_stamped.pose.orientation.z = transformStamped.transform.rotation.z;pose_stamped.pose.orientation.w = transformStamped.transform.rotation.w;pose_stamped.pose.position.x = transformStamped.transform.translation.x;pose_stamped.pose.position.y = transformStamped.transform.translation.y;pose_stamped.pose.position.z = transformStamped.transform.translation.z;if (is_stamped)publisher_stamp->publish(pose_stamped);elsepublisher_->publish(pose_stamped.pose);}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<geometry_msgs::msg::PoseStamped>::SharedPtr publisher_stamp;rclcpp::Publisher<geometry_msgs::msg::Pose>::SharedPtr publisher_;size_t count_;bool is_stamped = false;std::string base_frame = "base_link";std::string map_frame = "map";std::shared_ptr<tf2_ros::TransformListener> tf_listener_;std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
};int main(int argc, char *argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<RobotPosePublisher>());rclcpp::shutdown();return 0;
}
其中获取结果 buffer_.lookup_transform 获取map到base_link的坐标变化, 再发布robot_pose。
rosbridge_server
这个没有安装也需要安装下。
启动脚本
src/yahboomcar_nav/launch/map_cartographer_app_launch.xml
<launch><include file="$(find-pkg-share rosbridge_server)/launch/rosbridge_websocket_launch.xml"/><node name="laserscan_to_point_publisher" pkg="laserscan_to_point_publisher" exec="laserscan_to_point_publisher"/><include file="$(find-pkg-share yahboomcar_nav)/launch/map_cartographer_launch.py"/><include file="$(find-pkg-share robot_pose_publisher_ros2)/launch/robot_pose_publisher_launch.py"/><include file="$(find-pkg-share yahboom_app_save_map)/yahboom_app_save_map.launch.py"/>
</launch>
这里运行了以下几个launch文件和节点Node:
-
rosbridge_websocket_launch.xml:开启rosbridge服务相关节点,启动后,可以通过网络连接到ROS
-
laserscan_to_point_publisher:把雷达的点云转换发布到APP上进行可视化
-
map_cartographer_launch.py:cartographer建图程序
-
robot_pose_publisher_launch.py:小车位姿发布程序,小车位姿在APP进行可视化
-
yahboom_app_save_map.launch.py:保存地图的程序
程序功能说明
小车连接上代理,运行程序,打开手机上下载的【ROS Robot】app,输入小车的IP地址,选择ROS2,点击连接,即可连接上小车。通过滑动界面的轮盘可以控制小车,缓慢控制小车走完建图的区域,最后点击保存地图,小车会保存当前建好的地图。
启动
#小车代理
sudo docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:humble udp4 --port 8090 -v4
#摄像头代理(先启动代理再打开小车开关)
docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:humble udp4 --port 9999 -v4
首先启动小车处理底层数据程序,终端输入,
ros2 launch yahboomcar_bringup yahboomcar_bringup_launch.py
启动APP建图命令,终端输入,
ros2 launch yahboomcar_nav map_cartographer_app_launch.xml
#启动ESP32 摄像头
ros2 run yahboom_esp32_camera sub_img
手机APP显示如下图,输入小车的IP地址,【zh】表示中文,【en】表示英文;选择ROS2,下边的Video Tpoic选择/usb_cam/image_raw/compressed,最后点击【连接】

这个ip就是小车的配置ip。成功连接上后,跑一点地图显示如下,

有个问题就是app上不显示摄像头画面。我看亚博官网的文章上也没有显示,不知道为啥,猜测是控制小车的ip跟摄像头代理的不是1个。
另外的感受这个比较难操控小车,速度太快了,不如键盘好掌握。
以上。
相关文章:
亚博microros小车-原生ubuntu支持系列:20 ROS Robot APP建图
依赖工程 新建工程laserscan_to_point_publisher src/laserscan_to_point_publisher/laserscan_to_point_publisher/目录下新建文件laserscan_to_point_publish.py #!/usr/bin/env python3import rclpy from rclpy.node import Node from geometry_msgs.msg import PoseStam…...
Dockerfile构建容器镜像
Dockerfile 是一种文本格式的配置文件,用于自动化构建 Docker 镜像。它包含了一系列指令(命令),每个指令定义了容器镜像构建过程中的一步操作。通过Dockerfile,我们可以指定基础镜像、安装依赖、配置环境变量、复制文件…...
python 在包含类似字符\x16、\x12、\x某某的数组中将以\x开头的字符找出来的方法
话不多说直接看例子: import re# 原始列表 data [\x16, \x17, s, \x16, hello, \x1A]# 正则表达式匹配以 \x 开头的字符串 pattern r^\\x# 找出以 \x 开头的字符 result [item for item in data if isinstance(item, str) and re.match(pattern, repr(item)[1:-…...
Spring Bean 的生命周期介绍
Spring Bean 的生命周期涉及多个阶段,从实例化到销毁,在开发中我们可以通过各种接口和注解介入这些阶段来定制化自己的功能。以下是详细的生命周期流程: 1. Bean 的实例化(Instantiation) 方式:通过构造函…...
调用腾讯云批量文本翻译API翻译srt字幕
上一篇文章介绍了调用百度翻译API翻译日文srt字幕的方法。百度翻译API是get方式调用,参数都放在ur中,每次调用翻译文本长度除了接口限制外,还有url长度限制,而日文字符通过ur转码后会占9个字符长度,其实从这个角度来讲…...
车载软件架构 --- 软件定义汽车面向服务架构的应用迁移
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活…...
Baklib引领内容中台与人工智能技术的创新融合之路
内容概要 在数字化转型的浪潮中,各行业正在面临前所未有的挑战与机遇。内容中台作为一种新的概念,逐渐进入了企业的视野,它不仅是一个技术平台,更是提供了整合和管理内容的新思路。从根本上,内容中台旨在提升企业对信…...
想品客老师的第十一天:模块化开发
模块化概念 模块化开发可以提高代码的可维护性、可读性和复用性,同时降低开发和调试的复杂性,把业务根据功能分开写,解决变量命名的冲突,可以开放部分接口给类(例如调用模块里的一个函数)也更适合团队协作…...
接入DeepSeek大模型
接入DeepSeek 下载并安装Ollamachatbox 软件配置大模型 下载并安装Ollama 下载并安装Ollama, 使用参数ollama -v查看是否安装成功。 输入命令ollama list, 可以看到已经存在4个目录了。 输入命令ollama pull deepseek-r1:1.5b, 下载deepse…...
基于遗传算法的256QAM星座图的最优概率整形matlab仿真,对比优化前后整形星座图和误码率
目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印): GA优化曲线: 优化前后星座图对比 优化前后误码率对比 仿真操作步骤…...
JavaScript系列(57)--工程化实践详解
JavaScript工程化实践详解 🏗️ 今天,让我们深入探讨JavaScript的工程化实践。良好的工程化实践对于构建可维护、高质量的JavaScript项目至关重要。 工程化基础概念 🌟 💡 小知识:JavaScript工程化是指在JavaScript开…...
Linux-CentOS的yum源
1、什么是yum yum是CentOS的软件仓库管理工具。 2、yum的仓库 2.1、yum的远程仓库源 2.1.1、国内仓库 国内较知名的网络源(aliyun源,163源,sohu源,知名大学开源镜像等) 阿里源:https://opsx.alibaba.com/mirror 网易源:http://mirrors.1…...
【大数据技术】案例03:用户行为日志分析(python+hadoop+mapreduce+yarn+hive)
用户行为日志分析(python+hadoop+mapreduce+yarn+hive) 搭建完全分布式高可用大数据集群(VMware+CentOS+FinalShell) 搭建完全分布式高可用大数据集群(Hadoop+MapReduce+Yarn) 本机PyCharm远程连接虚拟机Python 搭建完全分布式高可用大数据集群(MySQL+Hive)...
LeetCode 0680.验证回文串 II:两侧向中间,不同就试删
【LetMeFly】680.验证回文串 II:两侧向中间,不同就试删 力扣题目链接:https://leetcode.cn/problems/valid-palindrome-ii/ 给你一个字符串 s,最多 可以从中删除一个字符。 请你判断 s 是否能成为回文字符串:如果能…...
第二十章 存储函数
目录 一、概述 二、语法 三、示例 一、概述 前面章节中,我们详细讲解了MySQL中的存储过程,掌握了存储过程之后,学习存储函数则肥仓简单,存储函数其实是一种特殊的存储过程,也就是有返回值的存储过程。存储函数的参数…...
架构规划之任务边界划分过程中承接分配
架构师在边界划分的过程中需要做什么事情呢?接下来,我们会讨论一些关于任务分配的 基础假设,以及由这些基础假设而带来的决策路径。 所谓任务边界划分,就是判定某个任务在多个承接方中,应该归属到哪个承接方的过程。…...
【C++】线程池实现
目录 一、线程池简介线程池的核心组件实现步骤 二、C11实现线程池源码 三、线程池源码解析1. 成员变量2. 构造函数2.1 线程初始化2.2 工作线程逻辑 3. 任务提交(enqueue方法)3.1 方法签名3.2 任务封装3.3 任务入队 4. 析构函数4.1 停机控制 5. 关键技术点解析5.1 完美转发实现5…...
vsnprintf的概念和使用案例
vsnprintf 是 C/C 标准库中用于格式化字符串的安全函数,属于 <stdio.h>(C)或 <cstdio>(C)头文件。它是 snprintf 的可变参数版本(v 表示 va_list),允许通过 va_list 处理…...
解读隐私保护工具 Fluidkey:如何畅游链上世界而不暴露地址?
作者:Techub 独家解读 撰文:Tia,Techub News 隐私不只是个人权利的象征,更是我们迈向透明、信任未来的重要过渡桥梁。如果你还未意识到隐私的重要性,推荐阅读 KeyMapDAO 的文章《「被出卖的自由」:我到底该…...
Linux环境Kanass安装配置简明教程
Kanass是一款国产开源免费的项目管理软件,本文将介绍如何快速在linux centos环境下安装配置,以快速上手。 1. 安装 以下以linux centos7下安装为例。 下载,下载地址:Kanass - 下载,下载Linux安装包如tiklab-kanass-1.0.4.rpm&am…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
