Simon Shi的小站

人工智能,机器学习, 强化学习,大模型,自动驾驶

0%

Pybind11 Python C++

环境安装

1
conda install pybind11 -c conda-forge

绑定一个c++的类

文件结构

1
2
3
src/Person.cpp
    /Person.h
    /p_person.cpp # pybind文件

Person.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Persion.cpp
#include "Person.h"

Person::Person() {
name = "";
age = 0;
}

Person::Person(string name, int age) {
this->name = name;
this->age = age;
}

Person::~Person() {

}

string Person::say_hello() {
return "hello, my name is " + this->name;
}

void Person::set_name(string name) {
this->name = name;
}
void Person::set_age(int age) {
this->age = age;
}

string Person::get_name() const {
return this->name;
}

int Person::get_age() const {
return this->age;
}

Person.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Persion.h
#pragma once

#include <string>
using namespace std;

class Person {
public:
Person();
Person(string name, int age);
~Person();

void set_name(string name);
void set_age(int age);
string get_name() const;
int get_age() const;

string say_hello();


private:
string name;
int age;

};

p_person.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "Person.h"

namespace py = pybind11;

PYBIND11_MODULE(person, m)
{
m.doc() = "Person module"; // optional module docstring

py::class_<Person>(m, "Person")
.def(py::init<>())
.def("get_name", &Person::get_name)
.def("set_name", &Person::set_name)
.def("get_age", &Person::get_age)
.def("set_age", &Person::set_age)
// .def("get_gender", &Person::get_gender)
// .def("set_gender", &Person::set_gender)
.def("say_hello", &Person::say_hello);
}

CMakeList.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)

project(PythonCPPExample)

# set(CMAKE_CXX_STANDARD 17)

# add_executable(PythonCPPExample main.cpp)

# find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
set(PYTHON_EXECUTABLE /root/anaconda3/envs/pytorch_1.7.1_cu92/bin/python3.8)
set(PYTHON_INCLUDE_DIR /root/anaconda3/envs/pytorch_1.7.1_cu92/include/python3.8)
set(PYTHON_LIBRARY /root/anaconda3/envs/pytorch_1.7.1_cu92/lib/libpython3.8.so)

find_package(PythonLibs 3 REQUIRED )
# 这个文件很重要
set(pybind11_DIR /root/anaconda3/envs/pytorch_1.7.1_cu92/share/cmake/pybind11/ ) # lib/python3.8/site-packages/pybind11 )
find_package(pybind11 REQUIRED)

message(STATUS " -XX- LIb path: ${PYTHON_INCLUDE_DIRS}")
message(STATUS " -XX- LIb path: ${pybind11_DIR}")
message(STATUS " -XX- LIb path: ${pybinSHARED ")

# add_subdirectory(src)

Pybind11_add_module(person
src/Person.cpp
d11_INCLUDE_DIRS}")

# .h 头文件路径
# include_directories(
# ${PYTHON_INCLUDE_DIRS}
# './src/'
# ${pybind11_INCLUDE_DIRS}
# )


# 库生成(动态SHARED,静态STATIC)
# add_library(person src/p_person.cpp)

# 目标文件依赖库
# target_link_libraries(person ${PYTHON_LIBRARIES} pybind11::pybind11)

# install(TARGETS person DESTINATION ${PYTHON_INSTALL_DIR}/site-packages/ )

编译

1
2
3
4
mkdir build
cd build
cmake ..
make

最终得到文件

person.cpython-38-x86_64-linux-gnu.so

python Call

  1. 当前环境下 CMakefile的环境下运行
1
2
3
4
5
6
7
8
9

import person

if __name__ == '__main__':
a = person.Person()
# a = person.Person('Tom', 18)
a.set_name("Tom")
a.set_age(19)
print(a.say_hello())

2、换个环境下运行

  • (此demo)只需要python版本一致,即可运行

ref

Python 中如何使用pybind11调用C++_pybind11 python调用c++-CSDN博客

pybind11:实现Python调用C++代码(入门)-CSDN博客

Thread

创建Thread方式

1 实例化Thread类创建线程
Thread类对象属性:

  • name :线程名。

  • ident :线程标识符。

  • daemon :布尔标志,表示这个线程是否是守护线程。

Thread类的构造方法的参数如下:

Thread(self,group=None,target=None,name=None,args=(),kwargs={},*,daemon=None)

  • group :默认为None,该参数不必理会。

  • target :指定线程要调用的方法或函数。

  • name :指定线程的别名。

  • args :指定target所指向的方法或者函数要用到的位置参数。

  • kwargs :指定target所指向的方法或者函数要用到的关键字参数。

  • daemon :指定线程是否为守护线程。
    示例:

1
2
3
4
5
6
7
8
9
import threading
def text():
print('thread %s is running.' % (threading.current_thread().name)
print('thread %s end.' % (threading.current_thread().name))
print('thread %s is running.' % (threading.current_thread().name))
t = threading.Thread(target=text)
t.start()
t.join()
print('thread %s end.' % (threading.current_thread().name))

运行结果:

1
2
3
4
thread MainThread is running.
thread Thread-1 is running.
thread Thread-1 end.
thread MainThread end.

<分析>

这个例子比较简单,只是简单的展示了创建线程的方法。每一个进程都有一个主线程,主线程是默认启动的,于是可以看到我们只实例化Thread类创建了一个线程,但运行结果却有两个线程。

另外可以发现每一个线程都有一个name,比方说MainThread(主线程)、Thread-1,这个name是哪来的呢?threading模块内有一个current_thread()函数,该函数总是返回当前线程的实例,那么current_thread().name不就是返回当前线程实例的name了么。另外,主线程默认的name就是MainThread,子线程如果没有在创建时指定,那么默认就是Thread-1、Thread-2…以此类推。

另外,启动一个线程的start()方法、用以表示现线程活动的run()方法、等待一个线程结束的join(timeout)方法,判断线程是否”存活”的is_alive()方法…这些方法都和进程中的这些方法完全类似,鉴于有关进程我已经在前两篇介绍过,这里就不再细说。

唯一提一点就是,进程中关于强制终止进程的terminate()方法不适用于Thread对象,唯独这一个不要在线程中去用。

最后,threading模块下除current_thread()之外的其他常用函数:

  • threading.enumerate() :返回一个包含正在运行的线程的list。
  • threading.activeCount() :返回正在运行的线程数量。等价于len(threading.enumerate())。

Thread类的一些其它方法:

  • getName() : 返回线程名。

  • setName() :设置线程名。

  • isDaemon() :判断该线程是否是守护线程,是返回True。

  • setDaemon() :设置线程的thread.daemon属性,该方法必须在线程start()之前调用。

以上方法都已经不常用,在Python的新版本中,设定name或者daemon属性可以不借助方法直接设定。

2 并发线程

进程和线程都可以并发执行,但线程由于划分尺度小,因此并发性相比于进程是要高的;另外,创建线程要更加简单,也更易于维护,由于多个线程共享进程的内存空间,这也使得线程更易于通信;除此之外,线程是Python直接支持的,这也简化了多线程的编程。

Python中的多线程也并不全是优点,事实上,Python中的多线程只是表面上的多线程,它和C++或者java中的不同,由于全局解释器锁GIL的存在,Python在一个时刻只会有一个线程在运行,这只是并发而不是并行。所以同样是多线程,java可以充分利用CPU的多核,但Python自始至终只会用到一个核,这就像单核CPU系统的多进程一样,Python的多线程是名不副实的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import threading
from time import sleep
def text(n):
for i in range(n):
print('thread %s is running >> %d' % (threading.current_thread().name,i))
if i == n-1:
print('thread %s end.' % (threading.current_thread().name))
break
sleep(2)
print('thread %s is running.' % (threading.current_thread().name))
t1 = threading.Thread(target=text,args=(4,))
t2 = threading.Thread(target=text,args=(4,))
t1.start()
t2.start()
t1.join()
t2.join()
print('thread %s end' % (threading.current_thread().name))

3.继承Thread类创建线程

通过继承Thread,重写Thread类的run()方法得到Thread类的子类,实例化子类也能创建线程。

1
2
3
4
5
6
7
8
9
10
11
12
import threading
class Another_thread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
print('thread %s is running.' % (threading.current_thread().name))
print('thread %s end.' % (threading.current_thread().name))
print('thread %s is running.' % (threading.current_thread().name))
t = Another_thread()
t.start()
t.join()
print('thread %s end.' % (threading.current_thread().name))

如上,通过重写Thread类的run()方法,就可以在接下来直接使用Thread的子类创建一个线程,当然也可以重写Thread类的__init__()方法,得到一个令人满意、功能完备的Thread类的子类。这种创建线程的方式更加适合面向对象编程

4. 实例化Thread,传入一个类实例

我们一般更倾向于使用上面介绍的两种方法创建线程,在第一种方法中,我们实例化Thread类,并传给它一个函数,第二种方法通过继承Thread得到子类创建线程,而接下来介绍的第三种方法,你会发现传入一个可调用的类实例同样可以创建线程。

这种创建线程的方法更加接近于面向对象的多线程编程,相比于传入函数而言具有更高的灵活性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import threading
from time import sleep
loops = [1,2,3]
class MyThread(object):
def __init__(self,func,args,name=''):
self.name = name
self.func = func
self.args = args
def __call__(self):
self.func(*self.args)
def text(x,sp):
print('thread %s is running >> %d.' % (threading.current_thread().name,x))
sleep(sp)
print('thread %s end.' % (threading.current_thread().name))
def main():
print('START!')
threads = []
for i in range(3):
t = threading.Thread(target=MyThread(text,(i,loops[i])))
threads.append(t)
for i in range(3):
threads[i].start()
for i in range(3):
threads[i].join()
print('ALL END!')
if __name__ == '__main__':
main()

START!
thread Thread-1 is running >> 0.
thread Thread-2 is running >> 1.
thread Thread-3 is running >> 2.
thread Thread-1 end.
thread Thread-2 end.
thread Thread-3 end.
ALL END!

<分析>

这种创建线程的方法比起前两种应该稍微复杂了一点,但实际上也就是相比于传入函数改为传入一个类,类完全由我们自己定制,构造方法__init__()可以添加任何我们需要的参数,之后注意这个特殊方法__call__(),它调用传入类的函数,并使用我们传入类的参数。

如果我们力求逻辑清晰地快速创建一个线程,任务要求不高,我们倾向于使用第一种创建线程的方法;如果我们需要完全面向对象的多线程编程,我们倾向于使用继承Thread类、实例化子类创建线程的方法,所以这第三种方法的地位有点尴尬,它并不被常用,这里了解一下知道就好。

ref:

Python 三种方法创建线程_python创建线程-CSDN博客

asyncio

asyncio 是 Python 中的一个异步 I/O 框架,它支持编写并发代码使用协程、多路复用 I/O 访问操作以及高级别并发原语(如事件循环、锁、条件变量等)。asyncio 使得 Python 能够高效地处理 I/O 密集型任务,如网络请求、文件 I/O 等

asyncio 的基本概念

  1. 事件循环(Event Loop):事件循环是 asyncio 的核心组件,它调度协程(coroutines)的执行。事件循环负责管理回调函数的注册和调度。

  2. 协程(Coroutines):协程是一种特殊的生成器函数,它可以暂停执行并恢复。协程在事件循环中被调度执行。

  3. FutureFuture 是一个特殊类型的对象,代表了将来某个时刻可能会得到的结果。Future 对象可以用来挂起协程的执行,直到结果可用为止。

  4. TaskTaskFuture 的子类,用于封装协程的执行。通过 asyncio.create_task()loop.create_task() 创建 Task 对象。

  5. 异步函数(Async Functions):定义以 async def 开头的函数称为异步函数。异步函数可以使用 await 关键字来等待另一个协程的完成。

asyncio 的基本用法

创建事件循环

1
2
3
4
5
6
7
8
9
10
11
12
13
import asyncio

async def hello_world():
print("Hello World!")

# 创建事件循环
loop = asyncio.get_event_loop()

# 运行协程
loop.run_until_complete(hello_world())

# 关闭事件循环
loop.close()

使用 async 和 await

1
2
3
4
5
6
7
8
9
10
11
12
import asyncio

async def sleep_and_print(seconds):
print(f"Sleeping for {seconds} seconds...")
await asyncio.sleep(seconds)
print("Done sleeping.")

async def main():
await sleep_and_print(1)

# 运行协程
asyncio.run(main())

创建任务

1

FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 APIs,基于 Python 3.7+ 的类型提示。FastAPI 是一个非常流行的选择,因为它提供了很多现代 Web 开发所需的功能,同时保持了简洁性和易用性。

FastAPI 的特点

  1. 声明式路由

    • 使用装饰器来定义 API 路由,使代码更加清晰和易于维护。
  2. 自动文档生成

    • 自动生成交互式文档(支持 Swagger UI 和 ReDoc),方便测试和文档编写。
  3. 异步支持

    • 支持异步请求处理,利用 Python 的 async/await 语法提高性能。
  4. 类型提示

    • 利用 Python 的类型提示来自动验证请求和响应数据。
  5. 依赖注入

    • 强大的依赖注入系统,简化了服务和组件的管理和注入。
  6. 性能

    • 快速响应时间和高吞吐量,适合构建高性能的 API。
Read more »

消息队列(Message Queue)是一种在分布式系统中进行异步通信的机制。它允许程序之间通过消息进行通信,而不需要直接调用对方。消息队列可以提高系统的可扩展性和容错能力,尤其是在处理高并发请求和异步任务时。

Python 中有几个常用的库可以用来实现消息队列,包括但不限于:

  1. RabbitMQ:使用 AMQP 协议的消息中间件。
  2. Redis:使用 Redis 的发布/订阅功能或列表数据结构实现消息队列。
  3. Kafka:高性能的分布式消息队列,常用于大数据处理。
  4. Celery:一个基于分布式消息传递的异步任务队列。

示例:使用 RabbitMQ

安装 RabbitMQ

首先,你需要安装 RabbitMQ 服务器。你可以从官方网站下载并安装 RabbitMQ,也可以使用包管理器(如 apt-get 或 yum)来安装。

安装 Pika 库

Pika 是一个 Python 库,用于与 RabbitMQ 交互。

1
pip install pika

发送消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pika

def send_message(message):
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

channel.basic_publish(exchange='',
routing_key='hello',
body=message)
print(" [x] Sent %r" % message)
connection.close()

if __name__ == '__main__':
send_message('Hello World!')

接收消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pika

def receive_message():
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
print(" [x] Received %r" % body)

channel.basic_consume(queue='hello',
on_message_callback=callback,
auto_ack=True)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

if __name__ == '__main__':
receive_message()

示例:使用 Redis

安装 Redis

确保你已经安装了 Redis 服务器。

安装 Redis 库

1
pip install redis

发送消息

1
2
3
4
5
6
7
8
9
import redis

def send_message(message):
r = redis.Redis(host='localhost', port=6379, db=0)
r.lpush('queue:messages', message)
print(" [x] Sent %r" % message)

if __name__ == '__main__':
send_message('Hello World!')

接收消息

1
2
3
4
5
6
7
8
9
10
11
import redis

def receive_message():
r = redis.Redis(host='localhost', port=6379, db=0)
while True:
message = r.rpop('queue:messages')
if message:
print(" [x] Received %r" % message.decode())

if __name__ == '__main__':
receive_message()

示例:使用 Celery

安装 Celery

1
pip install celery

配置 Celery

创建一个配置文件 celeryconfig.py

1
2
broker_url = 'redis://localhost:6379/0'
result_backend = 'redis://localhost:6379/0'

定义任务

创建一个任务文件 tasks.py

1
2
3
4
5
6
7
from celery import Celery

app = Celery('tasks', broker='redis://localhost:6379/0')

@app.task
def add(x, y):
return x + y

发送任务

1
2
3
4
5
from tasks import add

result = add.delay(4, 4)
print(result.ready()) # False
print(result.get(timeout=1)) # 8

启动 Celery 工作者

在命令行中启动 Celery 工作者:

1
celery -A tasks worker --loglevel=info

示例:使用 Kafka

安装 Kafka

确保你已经安装了 Kafka 服务器。

安装 Kafka-Python 库

1
pip install kafka-python

发送消息

1
2
3
4
5
6
7
8
9
10
11
12
from kafka import KafkaProducer
import json

def send_message(message):
producer = KafkaProducer(bootstrap_servers='localhost:9092',
value_serializer=lambda v: json.dumps(v).encode('utf-8'))
future = producer.send('my-topic', value=message)
result = future.get(timeout=60)
print(" [x] Sent %r" % message)

if __name__ == '__main__':
send_message({'key': 'value'})

接收消息

1
2
3
4
5
6
7
8
9
10
11
12
from kafka import KafkaConsumer
import json

def receive_message():
consumer = KafkaConsumer('my-topic',
bootstrap_servers='localhost:9092',
value_deserializer=lambda m: json.loads(m.decode('utf-8')))
for message in consumer:
print(" [x] Received %r" % message.value)

if __name__ == '__main__':
receive_message()

总结

以上是几个常用的消息队列实现示例。选择哪种消息队列取决于你的具体需求和场景:

  • RabbitMQ:适用于需要可靠的消息传递和复杂的路由规则的场景。
  • Redis:适用于简单的消息队列,易于部署和使用。
  • Kafka:适用于大数据处理和流式处理的场景。
  • Celery:适用于异步任务队列和定时任务。

如果你有其他具体的需求或问题,请告诉我,我可以进一步帮助你。

Download

Qwen

StableDiffusion

  • openai/clip-vit-large-patch14

  • /databig/ddz/sdiffusion/stable-diffusion-main/openai/clip-vit-large-patch14/

StableDiffusion2

  • stabilityai/stable-diffusion-2

SD-WebUI

ComfyUI

StoryDifusion

Flux

  • black-forest-labs/FLUX.1-dev

  • black-forest-labs/FLUX.1-schnell

  • google/t5-v1_1-xxl 90G

  • openai/clip-vit-large-patch14    6G

换脸

roop

Deep-live-cam

  • hacksider/deep-live-cam

Topic Example

STM32端

D:\xx\projects\robot\Ackerman-Car\ackerman-car-ros1\STM32和ROS串口通信分享\ALIENTEK MINISTM32 实验3 串口实验

D:\xx\projects\robot\Ackerman-Car\Ackerman-Car-ROS2-Driver\

ROS端

D:\shixx\projects\robot\Ackerman-Car\ackerman-car-ros1\STM32和ROS串口通信分享\topic_example\include\topic_example

1
2
3
4
5
cd catkin_ws_test
source devel/setup.bash
#启动功能包节点
rosrun topic_example publish_node
rosrun topic_example publish_n

Test

1
2
3
import serial
dev=serial.Serial('/dev/ttyUSB1',115200,timeout=0.5)
dev.readlines()

install ROS2

1
2
3
4
5
6
7
sudo apt update && sudo apt install curl gnupg2 lsb-release
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'
sudo apt update
sudo apt install ros-foxy-desktop
echo "source /opt/ros/foxy/setup.bash" >> ~/.bashrc
source ~/.bashrc
1
2
3
# sudo apt install colcon-common-extensions

sudo apt install python3-colcon-common-extensions

Build

1
2
3
4
5
6
colcon build --packages-select mbot_description
colcon build --packages-select mbot_bringup
colcon build --packages-select sllidar_ros2

# ros2 launch mbot_description gazebo.launch
source ~/ros2_ws/install/setup.bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ubuntu@ubuntu:~/ros2_ws$ colcon build --packages-select sllidar_ros2
[3.464s] WARNING:colcon.colcon_cmake.package_identification.cmake:Ignoring 'src/CMakeLists.txt' since it seems to be a toplevel CMake file generated by 'catkin_make'
Starting >>> sllidar_ros2
Finished <<< sllidar_ros2 [2.22s]

Summary: 1 package finished [4.26s]
ubuntu@ubuntu:~/ros2_ws$
ubuntu@ubuntu:~/ros2_ws$ colcon build --packages-select mbot_description
[3.425s] WARNING:colcon.colcon_cmake.package_identification.cmake:Ignoring 'src/CMakeLists.txt' since it seems to be a toplevel CMake file generated by 'catkin_make'
Starting >>> mbot_description
Finished <<< mbot_description [10.8s]

Summary: 1 package finished [12.8s]
ubuntu@ubuntu:~/ros2_ws$
1
2
3
4
5
6
sudo apt install software-properties-common
sudo add-apt-repository universe
# "deb http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main"
sudo add-apt-repository "deb http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main"
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -

BUG 思路 (foxy)

1、报错日志,百度+通义千问,给出了解决方案(但是代码实现跟本地不一致,硬套失败)

2、AI给的解决方案,都是成员函数的bind,没转换过来思路(总以为是参数的原因)

  • cmdCallback3函数不是一个类成员函数,因此不能使用std::bindthis结合的方式绑定到订阅器上。如果cmdCallback3确实是类的成员函数,那么它应该被声明为该类的一个成员,并且需要使用正确的绑定方式。
  • 实际上,不需要bind,就是原本的参数方式!!!!!!

3、阅读源码,(确认了calback3的参数与回调函数的模板一致)
/opt/ros/foxy/include/rclcpp/rclcpp.hpp

1
2
3
4
* - Subscription
* - rclcpp::Node::create_subscription()
* - rclcpp::Subscription
* - rclcpp/subscription.hpp

查到实现subscribe的代码:

/opt/ros/foxy/include/rclcpp/subscription.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/// Default constructor.
/**
* The constructor for a subscription is almost never called directly.
* Instead, subscriptions should be instantiated through the function
* rclcpp::create_subscription().
*/
///
Subscription(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rclcpp::QoS & qos,
AnySubscriptionCallback<CallbackMessageT, AllocatorT> callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename MessageMemoryStrategyT::SharedPtr message_memory_strategy,
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics = nullptr)

确认回调入口:AnySubscriptionCallback

AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_callback_;

    vim /opt/ros/foxy/include/rclcpp/any_subscription_callback.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
AnySubscriptionCallback


using SharedPtrCallback = std::function<void (const std::shared_ptr<MessageT>)>;
using SharedPtrWithInfoCallback =
std::function<void (const std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
// const 单个参数的,就这个了!!!!!!!!
using ConstSharedPtrCallback = std::function<void (const std::shared_ptr<const MessageT>)>;
using ConstSharedPtrWithInfoCallback =
std::function<void (const std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
using UniquePtrCallback = std::function<void (MessageUniquePtr)>;
using UniquePtrWithInfoCallback =
std::function<void (MessageUniquePtr, const rclcpp::MessageInfo &)>;

4、Finally

Build PASS

RUN

1
$ros2 launch mbot_bringup mbot_with_lidar_launch.py

car code

1
2
3
4
└──src
    ├── mbot_bringup
    ├── mbot_description
    └──sllidar_ros2-humble

编译

1
2
3
4
5
cd ~/ros2_ws
colcon build # 一起编译,若出现编译错误,可以单个单个编译功能包
colcon build --packages-select mbot_description
colcon build --packages-select mbot_bringup
colcon build --packages-select sllidar_ros2

刷新

1
2
3
source ~/ros2_ws/install/setup.bash
or
echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc

端口配置

1
2
cd /etc/udev/rules.d/
sudo vi 99-robot-usb.rules
1
2
3
4
5
6
KERNELS=="1-1", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523",
SYMLINK+="mbot",MODE:="0777"
KERNELS=="6-1", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60",
SYMLINK+="lidar",MODE:="0777"
KERNELS=="5-1", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60",
SYMLINK+="camera",MODE:="0777"

查看串口设备

1
2
3
udevadm info -a -p $(udevadm info -q path -n /dev/ttyUSB0)

ls /dev | grep ttyUSB

Serial

/dev/serial

1
2
3
4
5
6
7
8
looking at parent device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2':
KERNELS=="1-1.2"
ATTRS{idVendor}=="1a86"
ATTRS{devspec}=="(null)"
ATTRS{devpath}=="1.2"
ATTRS{idProduct}=="7523"

KERNELS=="1-1.2",ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523",SYMLINK+="mbot", MODE:="0777"
Lidar
1
2
3
KERNELS=="1-1.1", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="lidar", MODE:="0777"

KERNELS=="1-1", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="3431", SYMLINK+="lidar", MODE:="0777"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
looking at parent device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1':
KERNELS=="1-1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{speed}=="480"
ATTRS{authorized}=="1"
ATTRS{bMaxPower}=="100mA"
ATTRS{devspec}=="(null)"
ATTRS{product}=="USB2.0 Hub"
ATTRS{busnum}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bDeviceSubClass}=="00"
ATTRS{idVendor}=="2109"
ATTRS{bConfigurationValue}=="1"
ATTRS{rx_lanes}=="1"
ATTRS{tx_lanes}=="1"
ATTRS{quirks}=="0x0"
ATTRS{bcdDevice}=="0421"
ATTRS{bDeviceProtocol}=="01"
ATTRS{bmAttributes}=="e0"
ATTRS{configuration}==""
ATTRS{devnum}=="2"
ATTRS{devpath}=="1"
ATTRS{bDeviceClass}=="09"
ATTRS{urbnum}=="38"
ATTRS{version}==" 2.10"
ATTRS{maxchild}=="4"
ATTRS{idProduct}=="3431"
ATTRS{removable}=="unknown"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bNumInterfaces}==" 1"

运行

1.线路都链接正确且正常(单片机->香橙派)(激光雷达->香橙派)
2.单片机正常且烧录好程序

mbot_with_lidar_launch.py

1
ros2 launch mbot_bringup mbot_with_lidar_launch.py

[mbot_bringup-1] [INFO] [1666446425.232926736] [mbot_bringup]: y_:-0.006942
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1666446425.233090570] [mbot_bringup]: th_:338.204911
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1666446425.253658500] [mbot_bringup]: dt:0.021006
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1666446425.253865001] [mbot_bringup]: x_:-0.004255
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1666446425.253914877] [mbot_bringup]: y_:-0.006942

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
ubuntu@ubuntu:~/ros2_ws$ ros2 launch mbot_bringup mbot_with_lidar_launch.py
[INFO] [launch]: All log files can be found below /home/ubuntu/.ros/log/2024-08-26-14-58-42-813855-ubuntu-2195
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [mbot_bringup-1]: process started with pid [2197]
[INFO] [robot_state_publisher-2]: process started with pid [2199]
[INFO] [joint_state_publisher-3]: process started with pid [2201]
[INFO] [sllidar_node-4]: process started with pid [2203]
[robot_state_publisher-2] Parsing robot urdf xml string.
[robot_state_publisher-2] The root link base_link has an inertia specified in the URDF, but KDL does not support a root link with an inertia. As a w you can add an extra dummy link to your URDF.
[robot_state_publisher-2] Link lidar_link had 0 children
[robot_state_publisher-2] Link right_back_wheel had 0 children
[robot_state_publisher-2] Link left_back_wheel had 0 children
[robot_state_publisher-2] [INFO] [1724684323.340970547] [robot_state_publisher]: got segment base_link
[robot_state_publisher-2] [INFO] [1724684323.341309405] [robot_state_publisher]: got segment left_back_wheel
[robot_state_publisher-2] [INFO] [1724684323.341378643] [robot_state_publisher]: got segment lidar_link
[robot_state_publisher-2] [INFO] [1724684323.341410586] [robot_state_publisher]: got segment right_back_wheel
[sllidar_node-4] [INFO] [1724684323.348386895] [sllidar_node]: SLLidar running on ROS2 package SLLidar.ROS2 SDK Version:1.0.1, SLLIDAR SDK Version:2.
[sllidar_node-4] [INFO] [1724684323.402525357] [sllidar_node]: SLLidar S/N: DCA19DF1C3E39AC4C3E698F92C76340D
[sllidar_node-4] [INFO] [1724684323.402664611] [sllidar_node]: Firmware Ver: 1.27
[sllidar_node-4] [INFO] [1724684323.402700369] [sllidar_node]: Hardware Rev: 5
[sllidar_node-4] [INFO] [1724684323.454031586] [sllidar_node]: SLLidar health status : 0
[sllidar_node-4] [INFO] [1724684323.454176081] [sllidar_node]: SLLidar health status : OK.
[joint_state_publisher-3] [INFO] [1724684324.977991460] [joint_state_publisher]: Waiting for robot_description to be published on the robot_descripti.
[sllidar_node-4] [ERROR] [1724684327.485681561] [sllidar_node]: Can not start scan: 80008000!
^C[WARNING] [launch]: user interrupted with ctrl-c (SIGINT)
[mbot_bringup-1] [INFO] [1724684379.574944583] [rclcpp]: signal_handler(signal_value=2)
[mbot_bringup-1] [INFO] [1724684379.575556968] [mbot_bringup]: dt:56.246054
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1724684379.575648558] [mbot_bringup]: x_:0.000000
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1724684379.575687038] [mbot_bringup]: y_:0.000000
[mbot_bringup-1]
[mbot_bringup-1] [INFO] [1724684379.575721333] [mbot_bringup]: th_:0.000000
[mbot_bringup-1]
[robot_state_publisher-2] [INFO] [1724684379.576430992] [rclcpp]: signal_handler(signal_value=2)
[INFO] [robot_state_publisher-2]: process has finished cleanly [pid 2199]
[INFO] [mbot_bringup-1]: process has finished cleanly [pid 2197]
[INFO] [joint_state_publisher-3]: process has finished cleanly [pid 2201]
[sllidar_node-4] [INFO] [1724684381.596310322] [sllidar_node]: Stop motor
[INFO] [sllidar_node-4]: process has finished cleanly [pid 2203]

mob_launch.py

1
$ros2 launch mbot_bringup mbot_launch.py

log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[INFO] [launch]: All log files can be found below /home/ubuntu/.ros/log/2024-08-26-14-59-50-148695-ubuntu-2270
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [mbot_bringup-1]: process started with pid [2272]
[INFO] [robot_state_publisher-2]: process started with pid [2274]
[INFO] [joint_state_publisher-3]: process started with pid [2276]
[robot_state_publisher-2] Parsing robot urdf xml string.
[robot_state_publisher-2] The root link base_link has an inertia specified in the URDF, but KDL does not support a root link with an inertia. As a w you can add an extra dummy link to your URDF.
[robot_state_publisher-2] Link lidar_link had 0 children
[robot_state_publisher-2] Link right_back_wheel had 0 children
[robot_state_publisher-2] Link left_back_wheel had 0 children
[robot_state_publisher-2] [INFO] [1724684390.636906578] [robot_state_publisher]: got segment base_link
[robot_state_publisher-2] [INFO] [1724684390.637204512] [robot_state_publisher]: got segment left_back_wheel
[robot_state_publisher-2] [INFO] [1724684390.637258713] [robot_state_publisher]: got segment lidar_link
[robot_state_publisher-2] [INFO] [1724684390.637289175] [robot_state_publisher]: got segment right_back_wheel
[joint_state_publisher-3] [INFO] [1724684392.267731058] [joint_state_publisher]: Waiting for robot_description to be published on the robot_descripti.

Ref

Wexin 搭建ROS小车文章汇总 白茶 l 清欢 小白学移动机器人

# ros2-control系列教程(4) 控制管理器-运行整个框架

# 搭建ROS小车文章汇总

# ROS机器人启动功能包

# ROS机器人建图导航测试

# ROS机器人建图导航测试

ros app 下载、ros android app下载、ROS机器人控制app下载-CSDN博客

开源:

Releases · ROS-Mobile/ROS-Mobile-Android · GitHub

Other People:

android app控制ros机器人一_ros 机器人手机app-CSDN博客

linux系统下串口设备和串口号绑定_linux串口号重启后就会变-CSDN博客

ROS (Robot Operating System) 是一套用于机器人软件开发的框架,它并不是一个真正的操作系统,而是一套中间件。ROS 的第一个版本 ROS 1(通常简称为 ROS)是在 2010 年左右发布的,而 ROS 2 则是在 ROS 1 的基础上进行了重大的改进和重构,于 2015 年开始发布早期版本,并逐步发展成熟。

下面列出了 ROS 1 和 ROS 2 之间的一些主要区别:

1. 分布式通信模型

  • ROS 1 使用了基于 TCP/IP 的节点间通信方式,其中主要的通信机制包括:
    • Topics (主题): 发布/订阅模式。
    • Services (服务): 请求/响应模式。
    • Parameters (参数): 全局参数服务器。
    • Actions (动作): 长期任务的请求/响应模式。
  • ROS 2 也采用了类似的通信机制,但是底层通信协议从 ROS 1 的 TCP/IP 改为了 DDS (Data Distribution Service),这是一种更为先进的实时数据传输协议。DDS 的使用使得 ROS 2 能够更好地支持实时性要求高的场景,并且能够更轻松地跨越不同的网络拓扑结构。

2. 多线程支持

  • ROS 1 主要依赖于单线程模型,尽管可以通过多进程或多节点的方式来实现并发处理,但在单个节点内部实现多线程支持比较困难。
  • ROS 2 改进了对多线程的支持,使得在同一节点内可以更方便地利用多核处理器的能力。

3. 安全性和可靠性

  • ROS 1 在安全方面存在一些限制,例如没有内置的安全认证机制。
  • ROS 2 强化了安全特性,提供了认证、加密等机制,增强了系统的安全性。

4. 实时支持

  • ROS 1 在实时性方面有所欠缺,尤其是在网络延迟和确定性方面。
  • ROS 2 通过采用 DDS 作为通信基础,并且在设计上考虑了实时性需求,因此更适合于实时系统。

5. 语言支持

  • ROS 1 主要支持 C++ 和 Python,但大部分核心功能都是用 C++ 编写的。
  • ROS 2 同样支持 C++ 和 Python,但在设计上更加灵活,理论上可以支持更多的编程语言。

6. 系统架构

  • ROS 1 有一个名为 master 的中心节点来协调各个节点之间的通信。
  • ROS 2 采用了无中心节点的设计,所有节点都可以直接互相通信。

7. 内存管理

  • ROS 1 在内存管理和生命周期管理方面有一些局限性。
  • ROS 2 改进了这些方面,提供了更好的内存管理策略,比如自动垃圾回收。

8. 社区和支持

  • ROS 1 有着庞大的用户基础和丰富的软件包库。
  • ROS 2 的社区正在不断成长,虽然初期可能软件包较少,但随着 ROS 2 的成熟,越来越多的开发者和组织转向 ROS 2,它的生态系统也在迅速扩大。

9. 生命周期

  • ROS 1 已经停止官方支持和开发,但仍有一些社区支持的版本。
  • ROS 2 是目前 ROS 社区发展的重点方向,有长期的支持计划和稳定的版本更新。

10. 跨平台能力

  • ROS 1 支持多种操作系统,但主要针对 Linux。
  • ROS 2 在设计上更加注重跨平台兼容性,支持 Windows、macOS 和多种 Linux 发行版。

综上所述,ROS 2 是对 ROS 1 的全面升级,旨在解决 ROS 1 存在的问题,并增强其在现代机器人开发中的适用性和灵活性。如果您正计划新的机器人项目或者需要更高级的功能,建议选择 ROS 2。而对于已经使用 ROS 1 的项目,如果不需要上述提到的新特性,则可以继续使用 ROS 1,同时考虑未来的迁移计划。

ROS 区别

ROS Noetic中确实没有使用ament作为构建系统,而是使用了catkin工具。因此,您遇到的问题可能与ROS 2相关,而ROS Noetic则使用了不同的方法来处理订阅和回调函数。

  • 对于ROS Noetic:使用ros::NodeHandleros::Subscriberboost::function
  • 对于ROS 2:使用rclcpp::Noderclcpp::Subscriptionstd::bind