亚博microros小车-原生ubuntu支持系列:20 ROS Robot APP建图

news2025/2/7 22:20:13

依赖工程

新建工程laserscan_to_point_publisher

src/laserscan_to_point_publisher/laserscan_to_point_publisher/目录下新建文件laserscan_to_point_publish.py

#!/usr/bin/env python3

import 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 math

class 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_min
        angle_increment = msg.angle_increment
        laserscan = 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_min
        laser_points = Path()

        for distance in laserscan:
            x = distance * math.cos(angle + 135)#获取当前激光雷达数据点的的坐标值
            y = distance * math.sin(angle + 135)
            pose = PoseStamped()
            pose.pose.position.x = x
            pose.pose.position.y = y
            points.append(pose)
            angle += angle_increment
        laser_points.poses = points
 
        return laser_points


def 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);
		else
			publisher_ = 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);
		else
			publisher_->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,最后点击【连接】

image-20231219143224710

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

 

有个问题就是app上不显示摄像头画面。我看亚博官网的文章上也没有显示,不知道为啥,猜测是控制小车的ip跟摄像头代理的不是1个。

另外的感受这个比较难操控小车,速度太快了,不如键盘好掌握。

以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2294486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计Python+Vue.js游戏推荐系统 Steam游戏推荐系统 Django Flask 游 戏可视化 游戏数据分析 游戏大数据 爬虫

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

k8sollama部署deepseek-R1模型,内网无坑

这是目录 linux下载ollama模型文件下载到本地,打包迁移到k8s等无网络环境使用下载打包ollama镜像非k8s环境使用k8s部署访问方式非ollama运行deepseek模型linux下载ollama 下载后可存放其他服务器 curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o ollama-linu…

【Elasticsearch】nested聚合

在 Elasticsearch 中&#xff0c;嵌套聚合&#xff08;nestedaggregation&#xff09;的语法形式用于对嵌套字段&#xff08;nestedfields&#xff09;进行聚合操作。嵌套字段是 Elasticsearch 中的一种特殊字段类型&#xff0c;用于存储数组中的对象&#xff0c;这些对象需要独…

spy-debugger + Charles 调试移动端/内嵌小程序H5

简介说明&#xff1a; PC端可以用F12进行console等进行调试&#xff0c;但移动端App中使用webview就无法进行实时调试&#xff0c;针对这种情况 1. 安装 全局安装 spy-debugger sudo npm install spy-debugger -g // window不用加sudo2. spy-debugger 证书 其实spy-debugg…

【NLP 20、Encoding编码 和 Embedding嵌入】

目录 一、核心定义与区别 二、常见Encoding编码 (1) 独热编码&#xff08;One-Hot Encoding&#xff09; (2) 位置编码&#xff08;Positional Encoding&#xff09; (3) 标签编码&#xff08;Label Encoding&#xff09; (4) 注意事项 三、常见Embedding词嵌入 (1) 基础词嵌入…

深度学习模型可视化小工具wandb

1 概述 Wandb&#xff08;Weights & Biases&#xff0c;网址是https://wandb.ai&#xff09;是一个用于机器学习项目实验跟踪、可视化和管理的工具&#xff0c;旨在用户更有效地监控模型训练过程、优化性能&#xff0c;并分享和复现实验结果‌‌。对于使用者而言&#xff…

数据库系统概论的第六版与第五版的区别,附pdf

我用夸克网盘分享了「数据库系统概论第五六版资源」&#xff0c;点击链接即可保存。 链接&#xff1a;https://pan.quark.cn/s/21a278378dee 第6版教材修订的主要内容 为了保持科学性、先进性和实用性&#xff0c;在第5版教材基础上对全书内容进行了修改、更新和充实。 在科…

【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信

Kubernetes中Pod间的通信 本系列文章共3篇: 【Kubernetes Pod间通信-第1篇】在单个子网中使用underlay网络实现Pod到Pod的通信【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信(本文介绍)【Kubernetes Pod间通信-第3篇】Kubernetes中Pod与ClusterIP服务之间的通信…

软件设计模式

目录 一.创建型模式 抽象工厂 Abstract Factory 构建器 Builder 工厂方法 Factory Method 原型 Prototype 单例模式 Singleton 二.结构型模式 适配器模式 Adapter 桥接模式 Bridge 组合模式 Composite 装饰者模式 Decorator 外观模式 Facade 享元模式 Flyw…

vscode 如何通过Continue引入AI 助手deepseek

第一步&#xff1a; 在deepseek 官网上注册账号&#xff0c;得到APIKeys(deepseek官网地址) 创建属于自己的APIKey,然后复制这个key,(注意保存自己的key)! 第二步&#xff1a; 打开vscode,在插件市场安装Continue插件, 点击设置&#xff0c;添加deepseek模型&#xff0c;默认…

通过docker安装部署deepseek以及python实现

前提条件 Docker 安装:确保你的系统已经安装并正确配置了 Docker。可以通过运行 docker --version 来验证 Docker 是否安装成功。 网络环境:保证设备有稳定的网络连接,以便拉取 Docker 镜像和模型文件。 步骤一:拉取 Ollama Docker 镜像 Ollama 可以帮助我们更方便地管理…

iOS 音频录制、播放与格式转换

iOS 音频录制、播放与格式转换:基于 AVFoundation 和 FFmpegKit 的实现 在 iOS 开发中,音频处理是一个非常常见的需求,比如录音、播放音频、音频格式转换等。本文将详细解读一段基于 AVFoundation 和 FFmpegKit 的代码,展示如何实现音频录制、播放以及 PCM 和 AAC 格式之间…

RK3576——USB3.2 OTG无法识别到USB设备

问题&#xff1a;使用硬盘接入到OTG接口无热插拔信息&#xff0c;接入DP显示屏无法正常识别到显示设备&#xff0c;但是能通过RKDdevTool工具烧录系统。 问题分析&#xff1a;由于热插拔功能实现是靠HUSB311芯片完成的&#xff0c;因此需要先确保HUSB311芯片驱动正常工作。 1. …

【MySQL】语言连接

语言连接 一、下载二、mysql_get_client_info1、函数2、介绍3、示例 三、其他函数1、mysql_init2、mysql_real_connect3、mysql_query4、mysql_store_result5、mysql_free_result6、mysql_num_fields7、mysql_num_rows8、mysql_fetch_fields9、mysql_fetch_row10、mysql_close …

20240206 adb 连不上手机解决办法

Step 1: lsusb 确认电脑 usb 端口能识别设备 lsusb不知道设备有没有连上&#xff0c;就插拔一下&#xff0c;对比观察多了/少了哪个设备。 Step 2: 重启 adb server sudo adb kill-serversudo adb start-serveradb devices基本上就可以了&#xff5e; Reference https://b…

基于ansible部署elk集群

ansible部署 ELK部署 ELK常见架构 &#xff08;1&#xff09;ElasticsearchLogstashKibana&#xff1a;这种架构是最常见的一种&#xff0c;也是最简单的一种架构&#xff0c;这种架构通过Logstash收集日志&#xff0c;运用Elasticsearch分析日志&#xff0c;最后通过Kibana中…

Mac上搭建k8s环境——Minikube

1、在mac上安装Minikube可执行程序 brew cask install minikub 安装后使用minikube version命令查看版本 2、安装docker环境 brew install --cask --appdir/Applications docker #安装docker open -a Docker #启动docker 3、安装kubectl curl -LO https://storage.g…

MTGNN论文解读

模型架构 MTGNN 由多个模块组合而成&#xff0c;目标是捕捉多变量时间序列中的空间&#xff08;变量间&#xff09;和时间&#xff08;时序&#xff09;依赖。 图学习层&#xff1a;用于自适应地学习图的邻接矩阵&#xff0c;发现变量之间的关系。图卷积模块&#xff1a;根据邻…

C语言:函数栈帧的创建和销毁

目录 1.什么是函数栈帧2.理解函数栈帧能解决什么问题3.函数栈帧的创建和销毁的过程解析3.1 什么是栈3.2 认识相关寄存器和汇编指令3.3 解析函数栈帧的创建和销毁过程3.3.1 准备环境3.3.2 函数的调用堆栈3.3.3 转到反汇编3.3.4 函数栈帧的创建和销毁 1.什么是函数栈帧 在写C语言…

VSCode便捷开发

一、常用插件 Vue 3 Snippets、Vetur、Vue - Official 二、常用开发者工具 三、Vue中使用Element-UI 安装步骤&#xff1a; 1、在VSCode的终端执行如下指令&#xff1a; npm i element-ui -S 2、在main.js中全局引入&#xff1a; import Vue from vue; import ElementUI from …