1
0
forked from zbw/yiliao2026

Please enter the commit message for your changes. Lines starting

with '#' will be ignored, and an empty message aborts the commit.

On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
	modified:   .gitignore
	modified:   README.md
	new file:   bashes/auto-wifi-connect.service
	new file:   bashes/auto-wifi-connect.sh
	deleted:    keyboard_control.py
	new file:   my_model/image.png
	new file:   path_follower_demo.py
	new file:   scripts/PIDtracking.py
	new file:   scripts/__pycache__/publish_sine_path.cpython-310.pyc
	new file:   scripts/publish_sine_path.py
	modified:   src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml
	modified:   src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak
	new file:   src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak2
	modified:   src/origincar_base/config/ekf.yaml
	new file:   src/origincar_base/config/ekf.yaml.bak
	modified:   src/origincar_base/launch/base_serial.launch.py
	new file:   src/origincar_base/launch/base_serial.launch.py.bak
	modified:   src/origincar_base/launch/origincar_bringup.launch.py
	new file:   src/past_control/CMakeLists.txt
	new file:   src/past_control/config/past_control.yaml
	new file:   src/past_control/include/past_control/tools.h
	new file:   src/past_control/launch/past_control.launch.py
	new file:   src/past_control/msg/Obstacle.msg
	new file:   src/past_control/msg/ObstacleArray.msg
	new file:   src/past_control/package.xml
	new file:   src/past_control/src/lane_follower_node.cpp
	new file:   src/past_control/src/obstacle_detector_node.cpp
	new file:   src/past_control/src/racing_orchestrator.cpp
	new file:   src/planner/CMakeLists.txt
	new file:   src/planner/config/planner.yaml
	new file:   src/planner/launch/planner.launch.py
	new file:   src/planner/package.xml
	new file:   src/planner/src/planner_version.cpp
	modified:   src/qr_detection/src/qr_dete_depth.cpp
	new file:   src/racing_control/CMakeLists.txt
	new file:   src/racing_control/include/racing_control/racing_control.hpp
	new file:   src/racing_control/package.xml
	new file:   src/racing_control/src/racing_control.cpp
	modified:   src/vlm_detect/setup.py
	new file:   src/vlm_detect/vlm_detect/__pycache__/__init__.cpython-310.pyc
	new file:   src/vlm_detect/vlm_detect/__pycache__/tts_node.cpython-310.pyc
	new file:   src/vlm_detect/vlm_detect/test_publisher.py
	new file:   src/vlm_detect/vlm_detect/tts_node.py
	modified:   src/vlm_detect/vlm_detect/vlm_node.py
	new file:   tools/measure_turning_radius.py
	new file:   tools/set_volume.py
	new file:   tools/udp_to_cmdvel.py
	new file:   tools/windows_keyboard_control.py
	new file:   vlm_server.py
	new file:   "\350\260\203\350\257\225\350\256\260\345\275\225.Assets/1.png"
	renamed:    "\350\260\203\350\257\225\350\256\260\345\275\225.log" -> "\350\260\203\350\257\225\350\256\260\345\275\225.md"
This commit is contained in:
2026-06-22 17:16:45 +08:00
parent a9bfeef59b
commit 779b32362a
51 changed files with 3974 additions and 398 deletions

View File

@@ -21,6 +21,8 @@ setup(
entry_points={
'console_scripts': [
'vlm_node = vlm_detect.vlm_node:main',
'test_publisher = vlm_detect.test_publisher:main',
'tts_node = vlm_detect.tts_node:main',
],
},
)

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试发布者:发送图片和触发信号给 VLM 节点
用法: ros2 run vlm_detect test_publisher --ros-args -p image_path:="/path/to/image.jpg"
"""
import rclpy
from rclpy.node import Node
from std_msgs.msg import Int32, String
from sensor_msgs.msg import CompressedImage
import cv2
import time
class TestPublisher(Node):
def __init__(self):
super().__init__("test_publisher")
# 声明参数
self.declare_parameter(
"image_path", "/home/sunrise/yiliao_ws/my_model/image.png"
)
self.declare_parameter("interval", 5.0) # 每 N 秒触发一次
image_path = self.get_parameter("image_path").value
interval = self.get_parameter("interval").value
# 发布者
self.image_pub = self.create_publisher(CompressedImage, "/image_mjpeg", 10)
self.sign_pub = self.create_publisher(Int32, "/sign4return", 10)
# 订阅结果
self.result_sub = self.create_subscription(
String, "/vlm_result", self.result_callback, 10
)
# 加载图片
self.image_data = cv2.imread(image_path)
if self.image_data is None:
self.get_logger().error(f"无法读取图片: {image_path}")
raise FileNotFoundError(f"Image not found: {image_path}")
encode_params = [cv2.IMWRITE_JPEG_QUALITY, 90]
_, jpeg_data = cv2.imencode(".jpg", self.image_data, encode_params)
self.jpeg_bytes = jpeg_data.tobytes()
self.get_logger().info(f"已加载图片: {image_path}")
# 定时器
self.timer = self.create_timer(interval, self.timer_callback)
self.get_logger().info(f"每 {interval}s 发送一次图片和触发信号")
def timer_callback(self):
# 1. 发送压缩图像
img_msg = CompressedImage()
img_msg.header.stamp = self.get_clock().now().to_msg()
img_msg.format = "jpeg"
img_msg.data = self.jpeg_bytes
self.image_pub.publish(img_msg)
self.get_logger().info("已发送图片")
# 2. 等待一小段时间让订阅者收到图片
time.sleep(1.0)
# 3. 发送触发信号 (sign=9)
sign_msg = Int32()
sign_msg.data = 9
self.sign_pub.publish(sign_msg)
self.get_logger().info("已发送触发信号 (sign=9),等待 VLM 结果...")
def result_callback(self, msg):
self.get_logger().info(f"🔍 VLM 识别结果: {msg.data}")
def main(args=None):
rclpy.init(args=args)
node = TestPublisher()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
import rclpy, subprocess, requests, os
from rclpy.node import Node
from std_msgs.msg import String
VLM_HOST = "http://192.168.10.173:8000"
# USB Audio Device (Card 1)
AUDIO_SINK = "alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo"
AUDIO_ENV = {**os.environ, "PULSE_SINK": AUDIO_SINK}
class TTSNode(Node):
def __init__(self):
super().__init__("tts_node")
self.sub = self.create_subscription(String, "/vlm_result", self.callback, 10)
self.get_logger().info("TTS 播报节点已启动 (USB Audio Device, edge-tts 自然语音)")
def callback(self, msg):
text = msg.data
self.get_logger().info(f"播报: {text}")
try:
resp = requests.post(f"{VLM_HOST}/v1/tts",
json={"text": text, "voice": "zh-CN-XiaoxiaoNeural"}, timeout=60)
mp3 = "/tmp/tts_out.mp3"
with open(mp3, "wb") as f:
f.write(resp.content)
subprocess.Popen(["ffplay", "-nodisp", "-autoexit", mp3],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
env=AUDIO_ENV)
except Exception as e:
self.get_logger().error(f"TTS 失败,降级 espeak: {e}")
subprocess.Popen(["espeak-ng", "-v", "zh", "-s", "150", text],
env=AUDIO_ENV)
def main(args=None):
rclpy.init(args=args)
node = TTSNode()
try: rclpy.spin(node)
except KeyboardInterrupt: pass
finally: node.destroy_node(); rclpy.shutdown()
if __name__ == "__main__":
main()

View File

@@ -15,14 +15,14 @@ import numpy as np
class VLMProcessor(Node):
def __init__(self):
super().__init__('vlm_dtetct')
super().__init__('vlm_detect')
# 初始化 OpenAI 客户端
self.client = OpenAI(
base_url="http://192.168.1.103:8000/v1",
api_key="EMPTY"
base_url="http://192.168.10.173:8000/v1", # 本地 API 地址
api_key="EMPTY", # 不需要真实 API key
)
# ROS2 组件
self.bridge = CvBridge()
self.latest_image = None
@@ -31,7 +31,7 @@ class VLMProcessor(Node):
# 订阅图像话题
self.image_sub = self.create_subscription(
CompressedImage,
'/image',
'/image_mjpeg',
self.image_callback,
10
)
@@ -57,18 +57,20 @@ class VLMProcessor(Node):
"""保存最新的图像"""
with self.image_lock:
try:
# bridge = CvBridge()
np_arr = np.frombuffer(msg.data, np.uint8)
# 使用 OpenCV 解码图像
cv_image = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
# cv_image =bridge.imgmsg_to_cv2(msg,desired_encoding='bgr8')
self.latest_image = cv_image
self.get_logger().debug("recive picture")
except Exception as e:
self.get_logger().error("err picture")
self.get_logger().error(f"err picture: {e}")
def sign_callback(self, msg):
"""处理触发信号"""
if msg.data == 9:
self.get_logger().info("收到触发信号 (6),开始处理图像...")
self.get_logger().info(f"收到触发信号 ({msg.data}),开始处理图像...")
# 检查是否有可用图像
with self.image_lock:
@@ -79,12 +81,12 @@ class VLMProcessor(Node):
# 保存临时图像文件
temp_path = "/tmp/vlm_temp_image.jpg"
cv2.imwrite(temp_path, self.latest_image)
self.get_logger().info("已保存临时图像}")
self.get_logger().info(f"已保存临时图像: {temp_path}")
# 处理图像
try:
description = self.process_image(temp_path)
self.get_logger().info(f"图像描述结果:")
self.get_logger().info(f"图像描述结果: {description}")
# 发布结果
result_msg = String()
@@ -94,7 +96,7 @@ class VLMProcessor(Node):
# 清理临时文件
os.remove(temp_path)
except Exception as e:
self.get_logger().error(f"处理图像时出错:{e}")
self.get_logger().error(f"处理图像时出错: {e}")
def process_image(self, image_path):
"""使用 VLM 模型处理图像"""
@@ -106,26 +108,26 @@ class VLMProcessor(Node):
start_time = time.time()
response = self.client.chat.completions.create(
model="/home/yyh/vllm_test/model/InternVL3-1B",
model="./OpenGVLab/InternVL3-1B/",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "描述图片中的动漫病人"},
{"type": "text", "text": "描述图片中有一个病人的特征,字数控制在20字以内。"},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
},
},
]
}
],
max_tokens=100
max_tokens=100,
)
processing_time = time.time() - start_time
self.get_logger().info("VLM 处理耗时秒")
self.get_logger().info(f"VLM 处理耗时 {processing_time:.1f}s")
return response.choices[0].message.content