llama_ros
此代码库提供了一组 ROS 2 包,用于将 llama.cpp 集成到 ROS 2 中。通过使用 llama_ros 包,您可以轻松地将 llama.cpp 的强大优化功能集成到您的 ROS 2 项目中,运行基于 GGUF 的 LLMs 和 VLMs。
目录
相关项目
- chatbot_ros → 这个集成到 ROS 2 中的聊天机器人使用 whisper_ros 监听人类语音,并使用 llama_ros 生成响应。该聊天机器人由一个由 YASMIN 创建的状态机控制。
- explainable_ros → 一个用于解释机器人行为的 ROS 2 工具。通过集成 LangChain,日志被存储在向量数据库中。然后,应用 RAG 来检索用户问题所需的相关日志,并使用 llama_ros 回答。
安装
若要使用 CUDA 运行 llama_ros,首先,您必须安装 CUDA Toolkit。
$ cd ~/ros2_ws/src
$ git clone https://github.com/mgonzs13/llama_ros.git
$ pip3 install -r llama_ros/requirements.txt
$ cd ~/ros2_ws
$ rosdep install --from-paths src --ignore-src -r -y
$ colcon build --cmake-args -DGGML_CUDA=ON # 添加此项以支持 CUDA
用法
llama_cli
llama_ros 包含一些命令以加速在 ROS 2 生态系统中测试基于 GGUF 的 LLMs。如下命令已集成到 ROS 2 命令中:
launch
使用该命令从 YAML 文件启动一个 LLM。YAML 的配置用于像使用常规启动文件一样启动 LLM。以下是如何使用它的示例:
$ ros2 llama launch ~/ros2_ws/src/llama_ros/llama_bringup/params/StableLM-Zephyr.yaml
prompt
使用该命令向启动的 LLM 发送一个提示。该命令使用一个字符串作为提示,并具有以下参数:
- (
-t
,--temp
): 温度值 - (
--iamge-url
): 发送给 VLM 的图片 URL - (
--llava
): 是否提示 llava 而不是 llama - (
-r
,--reset
): 是否在提示前重置 LLM
以下是如何使用它的示例:
$ ros2 llama prompt "你知道 ROS 2 吗?" -t 0.0
启动文件
首先,您需要创建一个启动文件来使用 llama_ros 或 llava_ros。该启动文件将包含主要参数以从 HuggingFace 下载模型并进行配置。请看以下示例以及 预定义的启动文件。
llama_ros(Python 启动)
点击展开
from launch import LaunchDescription
from llama_bringup.utils import create_llama_launch
def generate_launch_description():
return LaunchDescription([
create_llama_launch(
n_ctx=2048, # LLM 的上下文范围(以 tokens 为单位)
n_batch=8, # 批量大小(以 tokens 为单位)
n_gpu_layers=0, # 加载到 GPU 中的层数
n_threads=1, # 线程数
n_predict=2048, # 最大 tokens,-1 表示无限制
model_repo="TheBloke/Marcoroni-7B-v3-GGUF", # Hugging Face 仓库
model_filename="marcoroni-7b-v3.Q4_K_M.gguf", # 仓库中的模型文件
system_prompt_type="alpaca" # 系统提示类型
)
])
$ ros2 launch llama_bringup marcoroni.launch.py
llama_ros(YAML 配置)
点击展开
n_ctx: 2048 # LLM 的上下文范围(以 tokens 为单位)
n_batch: 8 # 批量大小(以 tokens 为单位)
n_gpu_layers: 0 # 加载到 GPU 中的层数
n_threads: 1 # 线程数
n_predict: 2048 # 最大 tokens,-1 表示无限制
model_repo: "cstr/Spaetzle-v60-7b-GGUF" # Hugging Face 仓库
model_filename: "Spaetzle-v60-7b-q4-k-m.gguf" # 仓库中的模型文件
system_prompt_type: "Alpaca" # 系统提示类型
import os
from launch import LaunchDescription
from llama_bringup.utils import create_llama_launch_from_yaml
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
return LaunchDescription([
create_llama_launch_from_yaml(os.path.join(
get_package_share_directory("llama_bringup"), "params", "Spaetzle.yaml"))
])
$ ros2 launch llama_bringup spaetzle.launch.py
llava_ros(Python 启动)
点击展开
from launch import LaunchDescription
from llama_bringup.utils import create_llama_launch
def generate_launch_description():
return LaunchDescription([
create_llama_launch(
use_llava=True, # 启用 llava
embedding=False, # 禁用嵌入
n_ctx=8192, # LLM 的上下文范围(以 tokens 为单位),使用大范围以加载图片
n_batch=512, # 批量大小(以 tokens 为单位)
n_gpu_layers=33, # 加载到 GPU 中的层数
n_threads=1, # 线程数
n_predict=8192, # 最大 tokens,-1 表示无限制
model_repo="cjpais/llava-1.6-mistral-7b-gguf", # Hugging Face 仓库
model_filename="llava-v1.6-mistral-7b.Q4_K_M.gguf", # 仓库中的模型文件
mmproj_repo="cjpais/llava-1.6-mistral-7b-gguf", # Hugging Face 仓库
mmproj_filename="mmproj-model-f16.gguf", # 仓库中的 mmproj 文件
system_prompt_type="mistral" # 系统提示类型
)
])
$ ros2 launch llama_bringup llava.launch.py
llava_ros(YAML 配置)
点击展开
use_llava: True # 启用 llava
embedding: False # 禁用嵌入
n_ctx: 8192 # LLM 的上下文范围(以 tokens 为单位),使用大范围以加载图片
n_batch: 512 # 批量大小(以 tokens 为单位)
n_gpu_layers: 33 # 加载到 GPU 中的层数
n_threads: 1 # 线程数
n_predict: 8192 # 最大 tokens,-1 表示无限制
model_repo: "cjpais/llava-1.6-mistral-7b-gguf" # Hugging Face 仓库
model_filename: "llava-v1.6-mistral-7b.Q4_K_M.gguf" # 仓库中的模型文件
mmproj_repo: "cjpais/llava-1.6-mistral-7b-gguf" # Hugging Face 仓库
mmproj_filename: "mmproj-model-f16.gguf" # 仓库中的 mmproj 文件
system_prompt_type: "mistral" # 系统提示类型
def generate_launch_description():
return LaunchDescription([
create_llama_launch_from_yaml(os.path.join(
get_package_share_directory("llama_bringup"),
"params", "llava-1.6-mistral-7b-gguf.yaml"))
])
$ ros2 launch llama_bringup llava.launch.py
LoRA 适配器
启动 LLM 时可以使用 LoRA 适配器。利用 llama.cpp 的功能,您可以加载多个适配器并选择每个适配器的应用比例。以下是与 Phi-3 一起使用 LoRA 适配器的示例。
点击展开
n_ctx: 2048
n_batch: 8
n_gpu_layers: 0
n_threads: 1
n_predict: 2048
model_repo: "lmstudio-community/Phi-3.1-mini-4k-instruct-GGUF"
model_filename: "Phi-3.1-mini-4k-instruct-Q4_K_M.gguf"
lora_adapters:
- repo: "zhhan/adapter-Phi-3-mini-4k-instruct_code_writing"
filename: "Phi-3-mini-4k-instruct-adaptor-f16-code_writer.gguf"
scale: 0.5
- repo: "zhhan/adapter-Phi-3-mini-4k-instruct_summarization"
filename: "Phi-3-mini-4k-instruct-adaptor-f16-summarization.gguf"
scale: 0.5
system_prompt_type: "Phi-3"
ROS 2 客户端
llama_ros 和 llava_ros 均提供 ROS 2 接口以访问模型的主要功能。以下是如何在 ROS 2 节点中使用它们的一些示例。此外,请查看 llama_client_node.py 和 llava_client_node.py 示例。
Tokenize
点击展开
from rclpy.node import Node
from llama_msgs.srv import Tokenize
class ExampleNode(Node):
def __init__(self) -> None:
super().__init__("example_node")
# 创建客户端
self.srv_client = self.create_client(Tokenize, "/llama/tokenize")
# 创建请求
req = Tokenize.Request()
req.prompt = "示例文本"
# 调用 tokenize 服务
self.srv_client.wait_for_service()
res = self.srv_client.call(req)
tokens = res.tokens
嵌入
点击展开
from rclpy.node import Node
from llama_msgs.srv import Embeddings
class ExampleNode(Node):
def __init__(self) -> None:
super().__init__("example_node")
# 创建客户端
self.srv_client = self.create_client(Embeddings, "/llama/generate_embeddings")
# 创建请求
req = Embeddings.Request()
req.prompt = "示例文本"
req.normalize = True
# 调用嵌入服务
self.srv_client.wait_for_service()
res = self.srv_client.call(req)
embeddings = res.embeddings
点击展开
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from llama_msgs.action import GenerateResponse
class ExampleNode(Node):
def __init__(self) -> None:
super().__init__("example_node")
# 创建客户端
self.action_client = ActionClient(
self, GenerateResponse, "/llama/generate_response")
# 创建目标并设置采样配置
goal = GenerateResponse.Goal()
goal.prompt = self.prompt
goal.sampling_config.temp = 0.2
# 等待服务器并发送目标
self.action_client.wait_for_server()
send_goal_future = self.action_client.send_goal_async(goal)
# 再次等待服务器
rclpy.spin_until_future_complete(self, send_goal_future)
get_result_future = send_goal_future.result().get_result_async()
# 再次等待并获取结果
rclpy.spin_until_future_complete(self, get_result_future)
result: GenerateResponse.Result = get_result_future.result().result
生成响应(llava)
点击展开
import cv2
from cv_bridge import CvBridge
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from llama_msgs.action import GenerateResponse
class ExampleNode(Node):
def __init__(self) -> None:
super().__init__("example_node")
# 为图像创建一个cv桥
self.cv_bridge = CvBridge()
# 创建客户端
self.action_client = ActionClient(
self, GenerateResponse, "/llama/generate_response")
# 创建目标并设置采样配置
goal = GenerateResponse.Goal()
goal.prompt = self.prompt
goal.sampling_config.temp = 0.2
# 添加图像到目标
image = cv2.imread("/path/to/your/image", cv2.IMREAD_COLOR)
goal.image = self.cv_bridge.cv2_to_imgmsg(image)
# 等待服务器并发送目标
self.action_client.wait_for_server()
send_goal_future = self.action_client.send_goal_async(goal)
# 再次等待服务器
rclpy.spin_until_future_complete(self, send_goal_future)
get_result_future = send_goal_future.result().get_result_async()
# 再次等待并获取结果
rclpy.spin_until_future_complete(self, get_result_future)
result: GenerateResponse.Result = get_result_future.result().result
LangChain
有一个 llama_ros与LangChain的集成。因此,可以应用提示工程技术。下面是一个使用它的例子。
llama_ros (链)
点击展开
import rclpy
from llama_ros.langchain import LlamaROS
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
rclpy.init()
# 为LangChain创建llama_ros llm
llm = LlamaROS()
# 创建一个提示模板
prompt_template = "tell me a joke about {topic}"
prompt = PromptTemplate(
input_variables=["topic"],
template=prompt_template
)
# 使用llm和提示模板创建一个链
chain = prompt | llm | StrOutputParser()
# 运行链
text = chain.invoke({"topic": "bears"})
print(text)
rclpy.shutdown()
llama_ros (流)
点击展开
import rclpy
from llama_ros.langchain import LlamaROS
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
rclpy.init()
# 为LangChain创建llama_ros llm
llm = LlamaROS()
# 创建一个提示模板
prompt_template = "tell me a joke about {topic}"
prompt = PromptTemplate(
input_variables=["topic"],
template=prompt_template
)
# 使用llm和提示模板创建一个链
chain = prompt | llm | StrOutputParser()
# 运行链
for c in chain.stream({"topic": "bears"}):
print(c, flush=True, end="")
rclpy.shutdown()
llava_ros
点击展开
import rclpy
from llama_ros.langchain import LlamaROS
rclpy.init()
# 为LangChain创建llama_ros llm
llm = LlamaROS(namespace="llava")
# 绑定url_image
llm = llm.bind(image_url=image_url).stream("Describe the image")
image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
# 运行llm
for c in llm:
print(c, flush=True, end="")
rclpy.shutdown()
llama_ros_embeddings (RAG)
点击展开
import rclpy
from langchain_community.vectorstores import Chroma
from llama_ros.langchain import LlamaROSEmbeddings
rclpy.init()
# 为LangChain创建llama_ros嵌入
embeddings = LlamaROSEmbeddings()
# 创建一个向量数据库并分配它
db = Chroma(embedding_function=embeddings)
# 创建一个检索器
retriever = db.as_retriever(search_kwargs={"k": 5})
# 添加文本到数据库
db.add_texts(texts=["your_texts"])
# 检索文档
documents = retriever.get_relevant_documents("your_query")
print(documents)
rclpy.shutdown()
演示
llama_ros
$ ros2 launch llama_bringup marcoroni.launch.py
$ ros2 run llama_demos llama_demo_node --ros-args -p prompt:="your prompt"
https://github.com/mgonzs13/llama_ros/assets/25979134/9311761b-d900-4e58-b9f8-11c8efefdac4
llava_ros
$ ros2 launch llama_bringup llava.launch.py
$ ros2 run llama_demos llava_demo_node --ros-args -p prompt:="your prompt" -p image_url:="url of the image" -p use_image:="whether to send the image"
https://github.com/mgonzs13/llama_ros/assets/25979134/4a9ef92f-9099-41b4-8350-765336e3503c