改了下地图和部分规划参数
This commit is contained in:
BIN
src/map/nav2_costmap_binary 01.png
Normal file
BIN
src/map/nav2_costmap_binary 01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
BIN
src/map/nav2_costmap_binary 02.png
Normal file
BIN
src/map/nav2_costmap_binary 02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -112,7 +112,7 @@ controller_server:
|
|||||||
PreferForwardCritic:
|
PreferForwardCritic:
|
||||||
enabled: false
|
enabled: false
|
||||||
cost_power: 1
|
cost_power: 1
|
||||||
cost_weight: 7.0
|
cost_weight: 9.0
|
||||||
threshold_to_consider: 0.5
|
threshold_to_consider: 0.5
|
||||||
CostCritic:
|
CostCritic:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -225,7 +225,7 @@ global_costmap:
|
|||||||
inflation_layer:
|
inflation_layer:
|
||||||
plugin: "nav2_costmap_2d::InflationLayer"
|
plugin: "nav2_costmap_2d::InflationLayer"
|
||||||
cost_scaling_factor: 3.0
|
cost_scaling_factor: 3.0
|
||||||
inflation_radius: 0.30
|
inflation_radius: 0.35
|
||||||
always_send_full_costmap: True
|
always_send_full_costmap: True
|
||||||
global_costmap_client:
|
global_costmap_client:
|
||||||
ros__parameters:
|
ros__parameters:
|
||||||
@@ -251,10 +251,10 @@ planner_server:
|
|||||||
angle_quantization_bins: 72
|
angle_quantization_bins: 72
|
||||||
analytic_expansion_ratio: 3.5
|
analytic_expansion_ratio: 3.5
|
||||||
analytic_expansion_max_length: 3.0
|
analytic_expansion_max_length: 3.0
|
||||||
minimum_turning_radius: 0.40
|
minimum_turning_radius: 0.45
|
||||||
reverse_penalty: 2.0
|
reverse_penalty: 4.0
|
||||||
change_penalty: 1.0
|
change_penalty: 1.0
|
||||||
non_straight_penalty: 1.2
|
non_straight_penalty: 1.0
|
||||||
cost_penalty: 3.0
|
cost_penalty: 3.0
|
||||||
retrospective_penalty: 0.015
|
retrospective_penalty: 0.015
|
||||||
# 5 m covers the rolling planning horizon without the startup and memory
|
# 5 m covers the rolling planning horizon without the startup and memory
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ racing_control:
|
|||||||
auto_start: false
|
auto_start: false
|
||||||
frame_id: odom
|
frame_id: odom
|
||||||
use_post_qr_pose: false
|
use_post_qr_pose: false
|
||||||
|
split_qr_to_vlm_segment: true
|
||||||
enable_vlm_image_relay: false
|
enable_vlm_image_relay: false
|
||||||
enable_dynamic_replanning: false
|
enable_dynamic_replanning: false
|
||||||
enable_recovery: true
|
enable_recovery: true
|
||||||
@@ -42,7 +43,7 @@ racing_control:
|
|||||||
recovery_backup_distance: 0.04
|
recovery_backup_distance: 0.04
|
||||||
recovery_backup_timeout_sec: 0.2
|
recovery_backup_timeout_sec: 0.2
|
||||||
max_recovery_attempts: 2
|
max_recovery_attempts: 2
|
||||||
circle_goal_tolerance: 0.30
|
circle_goal_tolerance: 0.50
|
||||||
|
|
||||||
# VLM capture:
|
# VLM capture:
|
||||||
# stop - stop at the VLM waypoint, trigger capture, then continue immediately.
|
# stop - stop at the VLM waypoint, trigger capture, then continue immediately.
|
||||||
|
|||||||
@@ -186,6 +186,37 @@ inline std::vector<geometry_msgs::msg::PoseStamped> remainingWaypoints(
|
|||||||
return {waypoints.begin() + static_cast<std::ptrdiff_t>(next_waypoint_index), waypoints.end()};
|
return {waypoints.begin() + static_cast<std::ptrdiff_t>(next_waypoint_index), waypoints.end()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::vector<geometry_msgs::msg::PoseStamped> remainingWaypointsAfterProgress(
|
||||||
|
const std::vector<geometry_msgs::msg::PoseStamped> & waypoints,
|
||||||
|
const geometry_msgs::msg::PoseStamped & current,
|
||||||
|
const std::size_t next_waypoint_index,
|
||||||
|
const double tolerance)
|
||||||
|
{
|
||||||
|
return remainingWaypoints(
|
||||||
|
waypoints,
|
||||||
|
advanceReachedWaypointIndex(waypoints, current, next_waypoint_index, tolerance));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<geometry_msgs::msg::PoseStamped> routeWaypointsAfterQr(
|
||||||
|
const geometry_msgs::msg::PoseStamped & entry_pose,
|
||||||
|
const std::vector<geometry_msgs::msg::PoseStamped> & route_waypoints,
|
||||||
|
const std::size_t vlm_waypoint_index,
|
||||||
|
const bool split_at_vlm)
|
||||||
|
{
|
||||||
|
std::vector<geometry_msgs::msg::PoseStamped> segment;
|
||||||
|
segment.reserve(route_waypoints.size() + 1);
|
||||||
|
segment.push_back(entry_pose);
|
||||||
|
|
||||||
|
std::size_t route_end = route_waypoints.size();
|
||||||
|
if (split_at_vlm && vlm_waypoint_index + 1 < route_end) {
|
||||||
|
route_end = vlm_waypoint_index + 1;
|
||||||
|
}
|
||||||
|
segment.insert(
|
||||||
|
segment.end(), route_waypoints.begin(), route_waypoints.begin() +
|
||||||
|
static_cast<std::ptrdiff_t>(route_end));
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
|
||||||
inline std::vector<geometry_msgs::msg::PoseStamped> posesFromFlatDoubles(
|
inline std::vector<geometry_msgs::msg::PoseStamped> posesFromFlatDoubles(
|
||||||
const std::vector<double> & values, const std::string & frame_id)
|
const std::vector<double> & values, const std::string & frame_id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ def generate_launch_description():
|
|||||||
|
|
||||||
params_file = LaunchConfiguration("params_file")
|
params_file = LaunchConfiguration("params_file")
|
||||||
auto_start = LaunchConfiguration("auto_start")
|
auto_start = LaunchConfiguration("auto_start")
|
||||||
|
split_qr_to_vlm_segment = LaunchConfiguration("split_qr_to_vlm_segment")
|
||||||
enable_vlm_image_relay = LaunchConfiguration("enable_vlm_image_relay")
|
enable_vlm_image_relay = LaunchConfiguration("enable_vlm_image_relay")
|
||||||
vlm_image_input_topic = LaunchConfiguration("vlm_image_input_topic")
|
vlm_image_input_topic = LaunchConfiguration("vlm_image_input_topic")
|
||||||
vlm_image_output_topic = LaunchConfiguration("vlm_image_output_topic")
|
vlm_image_output_topic = LaunchConfiguration("vlm_image_output_topic")
|
||||||
@@ -29,6 +30,7 @@ def generate_launch_description():
|
|||||||
params_file,
|
params_file,
|
||||||
{
|
{
|
||||||
"auto_start": auto_start,
|
"auto_start": auto_start,
|
||||||
|
"split_qr_to_vlm_segment": split_qr_to_vlm_segment,
|
||||||
"enable_vlm_image_relay": enable_vlm_image_relay,
|
"enable_vlm_image_relay": enable_vlm_image_relay,
|
||||||
"vlm_image_input_topic": vlm_image_input_topic,
|
"vlm_image_input_topic": vlm_image_input_topic,
|
||||||
"vlm_image_output_topic": vlm_image_output_topic,
|
"vlm_image_output_topic": vlm_image_output_topic,
|
||||||
@@ -48,6 +50,11 @@ def generate_launch_description():
|
|||||||
default_value="false",
|
default_value="false",
|
||||||
description="Start the race immediately instead of waiting for SPACE",
|
description="Start the race immediately instead of waiting for SPACE",
|
||||||
),
|
),
|
||||||
|
DeclareLaunchArgument(
|
||||||
|
"split_qr_to_vlm_segment",
|
||||||
|
default_value="true",
|
||||||
|
description="Stop at the VLM waypoint before planning the final home segment",
|
||||||
|
),
|
||||||
DeclareLaunchArgument(
|
DeclareLaunchArgument(
|
||||||
"enable_vlm_image_relay",
|
"enable_vlm_image_relay",
|
||||||
default_value="false",
|
default_value="false",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -58,6 +59,7 @@ enum class Stage
|
|||||||
enum class RouteSegment
|
enum class RouteSegment
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
ToQr,
|
||||||
ToVlm,
|
ToVlm,
|
||||||
AfterVlm,
|
AfterVlm,
|
||||||
FullRoute
|
FullRoute
|
||||||
@@ -212,6 +214,7 @@ private:
|
|||||||
use_trajectory_guard_ = declare_parameter<bool>(
|
use_trajectory_guard_ = declare_parameter<bool>(
|
||||||
"use_trajectory_guard", defaultUseTrajectoryGuard());
|
"use_trajectory_guard", defaultUseTrajectoryGuard());
|
||||||
use_post_qr_pose_ = declare_parameter<bool>("use_post_qr_pose", false);
|
use_post_qr_pose_ = declare_parameter<bool>("use_post_qr_pose", false);
|
||||||
|
split_qr_to_vlm_segment_ = declare_parameter<bool>("split_qr_to_vlm_segment", true);
|
||||||
enable_vlm_image_relay_ = declare_parameter<bool>("enable_vlm_image_relay", false);
|
enable_vlm_image_relay_ = declare_parameter<bool>("enable_vlm_image_relay", false);
|
||||||
enable_dynamic_replanning_ = declare_parameter<bool>("enable_dynamic_replanning", true);
|
enable_dynamic_replanning_ = declare_parameter<bool>("enable_dynamic_replanning", true);
|
||||||
enable_recovery_ = declare_parameter<bool>("enable_recovery", true);
|
enable_recovery_ = declare_parameter<bool>("enable_recovery", true);
|
||||||
@@ -235,7 +238,7 @@ private:
|
|||||||
declare_parameter<double>("recovery_backup_timeout_sec", 0.2);
|
declare_parameter<double>("recovery_backup_timeout_sec", 0.2);
|
||||||
pass_through_vlm_trigger_radius_ =
|
pass_through_vlm_trigger_radius_ =
|
||||||
declare_parameter<double>("pass_through_vlm_trigger_radius", 0.35);
|
declare_parameter<double>("pass_through_vlm_trigger_radius", 0.35);
|
||||||
circle_goal_tolerance_ = declare_parameter<double>("circle_goal_tolerance", 0.30);
|
circle_goal_tolerance_ = declare_parameter<double>("circle_goal_tolerance", 0.50);
|
||||||
dynamic_replan_max_consecutive_failures_ =
|
dynamic_replan_max_consecutive_failures_ =
|
||||||
declare_parameter<int>("dynamic_replan_max_consecutive_failures", 3);
|
declare_parameter<int>("dynamic_replan_max_consecutive_failures", 3);
|
||||||
max_recovery_attempts_ = declare_parameter<int>("max_recovery_attempts", 2);
|
max_recovery_attempts_ = declare_parameter<int>("max_recovery_attempts", 2);
|
||||||
@@ -303,40 +306,55 @@ private:
|
|||||||
|
|
||||||
void startKeyboardThread()
|
void startKeyboardThread()
|
||||||
{
|
{
|
||||||
if (!isatty(STDIN_FILENO)) {
|
int keyboard_fd = STDIN_FILENO;
|
||||||
|
bool close_keyboard_fd = false;
|
||||||
|
if (!isatty(keyboard_fd)) {
|
||||||
|
keyboard_fd = open("/dev/tty", O_RDONLY);
|
||||||
|
close_keyboard_fd = keyboard_fd >= 0;
|
||||||
|
}
|
||||||
|
if (keyboard_fd < 0 || !isatty(keyboard_fd)) {
|
||||||
|
if (close_keyboard_fd) {
|
||||||
|
close(keyboard_fd);
|
||||||
|
}
|
||||||
RCLCPP_WARN(
|
RCLCPP_WARN(
|
||||||
get_logger(),
|
get_logger(),
|
||||||
"stdin is not a TTY; use auto_start:=true to start without keyboard");
|
"no usable TTY for SPACE start; run from an interactive ssh tty or set auto_start:=true");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboard_thread_ = std::thread(
|
keyboard_thread_ = std::thread(
|
||||||
[this]() {
|
[this, keyboard_fd, close_keyboard_fd]() {
|
||||||
termios old_termios {};
|
termios old_termios {};
|
||||||
if (tcgetattr(STDIN_FILENO, &old_termios) != 0) {
|
if (tcgetattr(keyboard_fd, &old_termios) != 0) {
|
||||||
|
if (close_keyboard_fd) {
|
||||||
|
close(keyboard_fd);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
termios raw = old_termios;
|
termios raw = old_termios;
|
||||||
raw.c_lflag &= static_cast<unsigned int>(~(ICANON | ECHO));
|
raw.c_lflag &= static_cast<unsigned int>(~(ICANON | ECHO));
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
|
tcsetattr(keyboard_fd, TCSANOW, &raw);
|
||||||
|
|
||||||
while (!stop_keyboard_.load()) {
|
while (!stop_keyboard_.load()) {
|
||||||
fd_set read_set;
|
fd_set read_set;
|
||||||
FD_ZERO(&read_set);
|
FD_ZERO(&read_set);
|
||||||
FD_SET(STDIN_FILENO, &read_set);
|
FD_SET(keyboard_fd, &read_set);
|
||||||
timeval timeout {};
|
timeval timeout {};
|
||||||
timeout.tv_sec = 0;
|
timeout.tv_sec = 0;
|
||||||
timeout.tv_usec = 200000;
|
timeout.tv_usec = 200000;
|
||||||
const int ready = select(STDIN_FILENO + 1, &read_set, nullptr, nullptr, &timeout);
|
const int ready = select(keyboard_fd + 1, &read_set, nullptr, nullptr, &timeout);
|
||||||
if (ready > 0 && FD_ISSET(STDIN_FILENO, &read_set)) {
|
if (ready > 0 && FD_ISSET(keyboard_fd, &read_set)) {
|
||||||
char c = 0;
|
char c = 0;
|
||||||
if (read(STDIN_FILENO, &c, 1) == 1 && c == ' ') {
|
if (read(keyboard_fd, &c, 1) == 1 && c == ' ') {
|
||||||
start_requested_.store(true);
|
start_requested_.store(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &old_termios);
|
tcsetattr(keyboard_fd, TCSANOW, &old_termios);
|
||||||
|
if (close_keyboard_fd) {
|
||||||
|
close(keyboard_fd);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,12 +422,14 @@ private:
|
|||||||
|
|
||||||
if (stage_ == Stage::ExecuteCirclePath && use_trajectory_guard_ && routeSegmentReached()) {
|
if (stage_ == Stage::ExecuteCirclePath && use_trajectory_guard_ && routeSegmentReached()) {
|
||||||
finishStage("route segment final pose reached");
|
finishStage("route segment final pose reached");
|
||||||
if (active_segment_ == RouteSegment::ToVlm) {
|
if (active_segment_ == RouteSegment::ToQr) {
|
||||||
|
runQrWait();
|
||||||
|
} else if (active_segment_ == RouteSegment::ToVlm) {
|
||||||
runVlmWait();
|
runVlmWait();
|
||||||
} else if (active_segment_ == RouteSegment::AfterVlm ||
|
} else if (active_segment_ == RouteSegment::AfterVlm ||
|
||||||
active_segment_ == RouteSegment::FullRoute)
|
active_segment_ == RouteSegment::FullRoute)
|
||||||
{
|
{
|
||||||
runSwitchToNormalProfile();
|
finishRace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,22 +540,13 @@ private:
|
|||||||
latest_qr_result_.clear();
|
latest_qr_result_.clear();
|
||||||
selected_direction_ = RouteDirection::Unknown;
|
selected_direction_ = RouteDirection::Unknown;
|
||||||
qr_detection_disabled_ = false;
|
qr_detection_disabled_ = false;
|
||||||
|
recovery_attempts_ = 0;
|
||||||
|
active_segment_ = RouteSegment::ToQr;
|
||||||
|
active_segment_waypoints_ = {qr_pose_};
|
||||||
|
active_segment_next_waypoint_index_ = 0;
|
||||||
publishSign(sign_profile_normal_);
|
publishSign(sign_profile_normal_);
|
||||||
publishSign(sign_qr_enable_);
|
publishSign(sign_qr_enable_);
|
||||||
startStage(Stage::NavigateToQr, navigation_timeout_sec_);
|
runRouteSegmentPlanning("start to QR pose");
|
||||||
sendNavigateGoal(
|
|
||||||
qr_pose_, [this](const bool ok) {
|
|
||||||
if (stage_ != Stage::NavigateToQr) {
|
|
||||||
RCLCPP_DEBUG(get_logger(), "stale QR navigation result ignored");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
finishStage(ok ? "reached " + poseSummary(qr_pose_) : "navigation failed");
|
|
||||||
if (!ok) {
|
|
||||||
failRace("failed to reach QR pose");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
runQrWait();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void runQrWait()
|
void runQrWait()
|
||||||
@@ -551,11 +562,9 @@ private:
|
|||||||
|
|
||||||
void runQrTransitNavigation()
|
void runQrTransitNavigation()
|
||||||
{
|
{
|
||||||
if (qrTransitTargetAfterRecognition(use_post_qr_pose_) == QrTransitTarget::PostQr) {
|
disableQrDetectionOnce();
|
||||||
runPostQrNavigation();
|
cancelActiveFollowGoal();
|
||||||
} else {
|
runSwitchToTask2Profile();
|
||||||
runEntryNavigation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void runPostQrNavigation()
|
void runPostQrNavigation()
|
||||||
@@ -613,37 +622,37 @@ private:
|
|||||||
latest_vlm_result_.clear();
|
latest_vlm_result_.clear();
|
||||||
vlm_capture_triggered_ = false;
|
vlm_capture_triggered_ = false;
|
||||||
recovery_attempts_ = 0;
|
recovery_attempts_ = 0;
|
||||||
if (vlm_capture_mode_ == VlmCaptureMode::PassThrough) {
|
const auto split_at_vlm = split_qr_to_vlm_segment_ &&
|
||||||
active_segment_ = RouteSegment::FullRoute;
|
vlm_capture_mode_ == VlmCaptureMode::Stop;
|
||||||
active_segment_waypoints_ = route.waypoints;
|
|
||||||
active_segment_next_waypoint_index_ = 0;
|
active_segment_ = split_at_vlm ? RouteSegment::ToVlm : RouteSegment::FullRoute;
|
||||||
runRouteSegmentPlanning("full route with pass-through VLM capture");
|
active_segment_waypoints_.clear();
|
||||||
return;
|
if (use_post_qr_pose_) {
|
||||||
|
active_segment_waypoints_.push_back(post_qr_pose_);
|
||||||
}
|
}
|
||||||
|
|
||||||
active_segment_ = RouteSegment::ToVlm;
|
const auto qr_segment = routeWaypointsAfterQr(
|
||||||
active_segment_waypoints_.assign(
|
entry_pose_, route.waypoints, vlm_index, split_at_vlm);
|
||||||
route.waypoints.begin(),
|
active_segment_waypoints_.insert(
|
||||||
route.waypoints.begin() + vlm_index + 1);
|
active_segment_waypoints_.end(), qr_segment.begin(), qr_segment.end());
|
||||||
|
if (!split_at_vlm) {
|
||||||
|
active_segment_waypoints_.push_back(route.home_pose);
|
||||||
|
}
|
||||||
active_segment_next_waypoint_index_ = 0;
|
active_segment_next_waypoint_index_ = 0;
|
||||||
runRouteSegmentPlanning("to VLM waypoint");
|
runRouteSegmentPlanning(split_at_vlm ? "QR to VLM waypoint" : "QR through full route to home");
|
||||||
}
|
}
|
||||||
|
|
||||||
void runRemainingRouteSegment()
|
void runRemainingRouteSegment()
|
||||||
{
|
{
|
||||||
const auto & route = selectedRoute();
|
const auto & route = selectedRoute();
|
||||||
const auto vlm_index = vlmWaypointIndex(route);
|
const auto vlm_index = vlmWaypointIndex(route);
|
||||||
if (vlm_index + 1 >= route.waypoints.size()) {
|
|
||||||
runSwitchToNormalProfile();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
recovery_attempts_ = 0;
|
recovery_attempts_ = 0;
|
||||||
active_segment_ = RouteSegment::AfterVlm;
|
active_segment_ = RouteSegment::AfterVlm;
|
||||||
active_segment_waypoints_.assign(
|
active_segment_waypoints_ = remainingWaypoints(route.waypoints, vlm_index + 1);
|
||||||
route.waypoints.begin() + vlm_index + 1,
|
active_segment_waypoints_.push_back(route.home_pose);
|
||||||
route.waypoints.end());
|
|
||||||
active_segment_next_waypoint_index_ = 0;
|
active_segment_next_waypoint_index_ = 0;
|
||||||
runRouteSegmentPlanning("after VLM waypoint");
|
publishSign(sign_profile_normal_);
|
||||||
|
runRouteSegmentPlanning("VLM to home");
|
||||||
}
|
}
|
||||||
|
|
||||||
void runRouteSegmentPlanning(const std::string & label)
|
void runRouteSegmentPlanning(const std::string & label)
|
||||||
@@ -719,12 +728,14 @@ private:
|
|||||||
handleRouteExecutionFailure("route segment FollowPath failed");
|
handleRouteExecutionFailure("route segment FollowPath failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (active_segment_ == RouteSegment::ToVlm) {
|
if (active_segment_ == RouteSegment::ToQr) {
|
||||||
|
runQrWait();
|
||||||
|
} else if (active_segment_ == RouteSegment::ToVlm) {
|
||||||
runVlmWait();
|
runVlmWait();
|
||||||
} else if (active_segment_ == RouteSegment::AfterVlm ||
|
} else if (active_segment_ == RouteSegment::AfterVlm ||
|
||||||
active_segment_ == RouteSegment::FullRoute)
|
active_segment_ == RouteSegment::FullRoute)
|
||||||
{
|
{
|
||||||
runSwitchToNormalProfile();
|
finishRace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -780,9 +791,7 @@ private:
|
|||||||
|
|
||||||
void maybeTriggerPassThroughVlmCapture()
|
void maybeTriggerPassThroughVlmCapture()
|
||||||
{
|
{
|
||||||
if (vlm_capture_mode_ != VlmCaptureMode::PassThrough ||
|
if (active_segment_ != RouteSegment::FullRoute || vlm_capture_triggered_) {
|
||||||
active_segment_ != RouteSegment::FullRoute || vlm_capture_triggered_)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,18 +836,19 @@ private:
|
|||||||
|
|
||||||
last_dynamic_replan_time_ = now();
|
last_dynamic_replan_time_ = now();
|
||||||
dynamic_replan_in_flight_ = true;
|
dynamic_replan_in_flight_ = true;
|
||||||
runDynamicRouteReplanning();
|
runDynamicRouteReplanning(*current);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runDynamicRouteReplanning()
|
void runDynamicRouteReplanning(const geometry_msgs::msg::PoseStamped & current)
|
||||||
{
|
{
|
||||||
if (!compute_path_client_->wait_for_action_server(200ms)) {
|
if (!compute_path_client_->wait_for_action_server(200ms)) {
|
||||||
onDynamicReplanFailed("ComputePathThroughPoses action server is not available");
|
onDynamicReplanFailed("ComputePathThroughPoses action server is not available");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto replan_goals = remainingWaypoints(
|
const auto replan_goals = remainingWaypointsAfterProgress(
|
||||||
active_segment_waypoints_, active_segment_next_waypoint_index_);
|
active_segment_waypoints_, current, active_segment_next_waypoint_index_,
|
||||||
|
circle_goal_tolerance_);
|
||||||
if (replan_goals.empty()) {
|
if (replan_goals.empty()) {
|
||||||
dynamic_replan_in_flight_ = false;
|
dynamic_replan_in_flight_ = false;
|
||||||
return;
|
return;
|
||||||
@@ -914,6 +924,16 @@ private:
|
|||||||
reason,
|
reason,
|
||||||
[this]() {
|
[this]() {
|
||||||
dynamic_replan_consecutive_failures_ = 0;
|
dynamic_replan_consecutive_failures_ = 0;
|
||||||
|
const auto current = currentPoseFromOdom();
|
||||||
|
if (current) {
|
||||||
|
active_segment_waypoints_ = remainingWaypointsAfterProgress(
|
||||||
|
active_segment_waypoints_, *current, active_segment_next_waypoint_index_,
|
||||||
|
circle_goal_tolerance_);
|
||||||
|
} else {
|
||||||
|
active_segment_waypoints_ = remainingWaypoints(
|
||||||
|
active_segment_waypoints_, active_segment_next_waypoint_index_);
|
||||||
|
}
|
||||||
|
active_segment_next_waypoint_index_ = 0;
|
||||||
runRouteSegmentPlanning("after recovery");
|
runRouteSegmentPlanning("after recovery");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1239,7 +1259,10 @@ private:
|
|||||||
RCLCPP_INFO(
|
RCLCPP_INFO(
|
||||||
get_logger(), "QR result received: %s -> %s",
|
get_logger(), "QR result received: %s -> %s",
|
||||||
latest_qr_result_.c_str(), selectedRoute().label.c_str());
|
latest_qr_result_.c_str(), selectedRoute().label.c_str());
|
||||||
if (stage_ == Stage::NavigateToQr || stage_ == Stage::WaitForQr) {
|
if (stage_ == Stage::NavigateToQr || stage_ == Stage::WaitForQr ||
|
||||||
|
((stage_ == Stage::ComputeCirclePath || stage_ == Stage::ExecuteCirclePath) &&
|
||||||
|
active_segment_ == RouteSegment::ToQr))
|
||||||
|
{
|
||||||
finishStage("QR result: " + latest_qr_result_ + " -> " + selectedRoute().label);
|
finishStage("QR result: " + latest_qr_result_ + " -> " + selectedRoute().label);
|
||||||
runQrTransitNavigation();
|
runQrTransitNavigation();
|
||||||
}
|
}
|
||||||
@@ -1260,6 +1283,7 @@ private:
|
|||||||
bool auto_start_{false};
|
bool auto_start_{false};
|
||||||
bool use_trajectory_guard_{false};
|
bool use_trajectory_guard_{false};
|
||||||
bool use_post_qr_pose_{false};
|
bool use_post_qr_pose_{false};
|
||||||
|
bool split_qr_to_vlm_segment_{true};
|
||||||
bool enable_vlm_image_relay_{false};
|
bool enable_vlm_image_relay_{false};
|
||||||
bool enable_dynamic_replanning_{true};
|
bool enable_dynamic_replanning_{true};
|
||||||
bool enable_recovery_{true};
|
bool enable_recovery_{true};
|
||||||
|
|||||||
@@ -140,11 +140,11 @@ TEST(RacingControlHelpers, AdvancesNextWaypointOnlyAfterItIsReached)
|
|||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
racing_control::advanceReachedWaypointIndex(
|
racing_control::advanceReachedWaypointIndex(
|
||||||
waypoints, racing_control::poseFromXYYaw(0.65, 0.0, 0.0, "map"), 1, 0.30),
|
waypoints, racing_control::poseFromXYYaw(0.40, 0.0, 0.0, "map"), 1, 0.50),
|
||||||
1U);
|
1U);
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
racing_control::advanceReachedWaypointIndex(
|
racing_control::advanceReachedWaypointIndex(
|
||||||
waypoints, racing_control::poseFromXYYaw(1.05, 0.0, 0.0, "map"), 1, 0.30),
|
waypoints, racing_control::poseFromXYYaw(1.05, 0.0, 0.0, "map"), 1, 0.50),
|
||||||
2U);
|
2U);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +163,52 @@ TEST(RacingControlHelpers, DynamicReplanningKeepsRemainingUnreachedWaypoints)
|
|||||||
EXPECT_TRUE(racing_control::remainingWaypoints(waypoints, waypoints.size() + 1).empty());
|
EXPECT_TRUE(racing_control::remainingWaypoints(waypoints, waypoints.size() + 1).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RacingControlHelpers, TrimsPassedWaypointsBeforeReplanning)
|
||||||
|
{
|
||||||
|
const auto waypoints = racing_control::posesFromFlatDoubles(
|
||||||
|
{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0},
|
||||||
|
"map");
|
||||||
|
|
||||||
|
const auto trimmed = racing_control::remainingWaypointsAfterProgress(
|
||||||
|
waypoints, racing_control::poseFromXYYaw(1.02, 0.0, 0.0, "map"), 1, 0.50);
|
||||||
|
|
||||||
|
ASSERT_EQ(trimmed.size(), 1U);
|
||||||
|
EXPECT_DOUBLE_EQ(trimmed[0].pose.position.x, 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RacingControlHelpers, BuildsQrToVlmSegmentThroughEntry)
|
||||||
|
{
|
||||||
|
const std::string frame({'m', 'a', 'p'});
|
||||||
|
const auto entry = racing_control::poseFromXYYaw(10.0, 0.0, 0.0, frame);
|
||||||
|
const auto route = racing_control::posesFromFlatDoubles(
|
||||||
|
{1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 0.0},
|
||||||
|
frame);
|
||||||
|
|
||||||
|
const auto segment = racing_control::routeWaypointsAfterQr(entry, route, 1, true);
|
||||||
|
|
||||||
|
ASSERT_EQ(segment.size(), 3U);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[0].pose.position.x, 10.0);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[1].pose.position.x, 1.0);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[2].pose.position.x, 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RacingControlHelpers, CanConnectQrThroughAllRemainingRoute)
|
||||||
|
{
|
||||||
|
const std::string frame({'m', 'a', 'p'});
|
||||||
|
const auto entry = racing_control::poseFromXYYaw(10.0, 0.0, 0.0, frame);
|
||||||
|
const auto route = racing_control::posesFromFlatDoubles(
|
||||||
|
{1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 0.0},
|
||||||
|
frame);
|
||||||
|
|
||||||
|
const auto segment = racing_control::routeWaypointsAfterQr(entry, route, 1, false);
|
||||||
|
|
||||||
|
ASSERT_EQ(segment.size(), 4U);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[0].pose.position.x, 10.0);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[1].pose.position.x, 1.0);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[2].pose.position.x, 2.0);
|
||||||
|
EXPECT_DOUBLE_EQ(segment[3].pose.position.x, 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(RacingControlHelpers, RecoveryBackupStopsByDistanceOrTimeout)
|
TEST(RacingControlHelpers, RecoveryBackupStopsByDistanceOrTimeout)
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(racing_control::recoveryBackupComplete(0.04, 0.04, 0.1, 0.2));
|
EXPECT_TRUE(racing_control::recoveryBackupComplete(0.04, 0.04, 0.1, 0.2));
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ vlm_detect:
|
|||||||
image_max_dim: 128
|
image_max_dim: 128
|
||||||
image_topic: /image
|
image_topic: /image
|
||||||
max_tokens: 30
|
max_tokens: 30
|
||||||
prompt_text: 图中是一个2D动漫插画风格的医院病房,有一个病人。请描述这个病人的状态。不要描述边框、背景、环境。20字以内。
|
prompt_text: 请描述这个病人的状态。不要描述边框、背景。15字以内。
|
||||||
result_topic: /vlm_result
|
result_topic: /vlm_result
|
||||||
temperature: 0.1
|
temperature: 0.1
|
||||||
trigger_sign: 9
|
trigger_sign: 9
|
||||||
trigger_topic: /sign4return
|
trigger_topic: /sign4return
|
||||||
vlm_host: http://192.168.175.111:8000
|
vlm_host: http://192.168.175.64:8000
|
||||||
vlm_model: /home/wisdom/models/gguf/Qwen2-VL-2B-Instruct-Q4_K_M.gguf
|
vlm_model: /home/wisdom/models/gguf/Qwen2-VL-2B-Instruct-Q4_K_M.gguf
|
||||||
|
|||||||
@@ -35,15 +35,16 @@ def generate_launch_description():
|
|||||||
default_value=PathJoinSubstitution([
|
default_value=PathJoinSubstitution([
|
||||||
get_package_share_directory('vlm_detect'), 'config', 'vlm_detect.yaml']))
|
get_package_share_directory('vlm_detect'), 'config', 'vlm_detect.yaml']))
|
||||||
|
|
||||||
declare_vlm_host = DeclareLaunchArgument('vlm_host', default_value='http://192.168.175.111:8000')
|
# RDK X5 BPU server (default). For WSL server, use: vlm_host:=http://192.168.175.111:8000
|
||||||
declare_vlm_model = DeclareLaunchArgument('vlm_model', default_value='/home/wisdom/models/gguf/Qwen2-VL-2B-Instruct-Q4_K_M.gguf')
|
declare_vlm_host = DeclareLaunchArgument('vlm_host', default_value='http://192.168.175.64:8000')
|
||||||
|
declare_vlm_model = DeclareLaunchArgument('vlm_model', default_value='internvl2.5-qwen2.5-0.5b')
|
||||||
declare_image_topic = DeclareLaunchArgument('image_topic', default_value='/image')
|
declare_image_topic = DeclareLaunchArgument('image_topic', default_value='/image')
|
||||||
declare_trigger_topic = DeclareLaunchArgument('trigger_topic', default_value='/sign4return')
|
declare_trigger_topic = DeclareLaunchArgument('trigger_topic', default_value='/sign4return')
|
||||||
declare_trigger_sign = DeclareLaunchArgument('trigger_sign', default_value='9')
|
declare_trigger_sign = DeclareLaunchArgument('trigger_sign', default_value='9')
|
||||||
declare_result_topic = DeclareLaunchArgument('result_topic', default_value='/vlm_result')
|
declare_result_topic = DeclareLaunchArgument('result_topic', default_value='/vlm_result')
|
||||||
declare_prompt_text = DeclareLaunchArgument('prompt_text', default_value='图中是一个2D动漫插画风格的医院病房,有一个病人。请描述这个病人的状态。不要描述边框、背景、环境。20字以内。')
|
declare_prompt_text = DeclareLaunchArgument('prompt_text', default_value='请描述这个病人的状态。不要描述边框、背景。15字以内。')
|
||||||
declare_max_tokens = DeclareLaunchArgument('max_tokens', default_value='100')
|
declare_max_tokens = DeclareLaunchArgument('max_tokens', default_value='30')
|
||||||
declare_image_max_dim = DeclareLaunchArgument('image_max_dim', default_value='128')
|
declare_image_max_dim = DeclareLaunchArgument('image_max_dim', default_value='448')
|
||||||
declare_audio_sink = DeclareLaunchArgument('audio_sink',
|
declare_audio_sink = DeclareLaunchArgument('audio_sink',
|
||||||
default_value='alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo')
|
default_value='alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo')
|
||||||
declare_tts_speed = DeclareLaunchArgument('tts_speed', default_value='1.5')
|
declare_tts_speed = DeclareLaunchArgument('tts_speed', default_value='1.5')
|
||||||
|
|||||||
113
src/vlm_detect/launch/vlm_detect.launch.py.wsl_bak
Normal file
113
src/vlm_detect/launch/vlm_detect.launch.py.wsl_bak
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument, LogInfo
|
||||||
|
from launch.conditions import IfCondition
|
||||||
|
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
use_vlm = LaunchConfiguration('use_vlm')
|
||||||
|
use_tts = LaunchConfiguration('use_tts')
|
||||||
|
use_qr_tts = LaunchConfiguration('use_qr_tts')
|
||||||
|
config_file = LaunchConfiguration('config_file')
|
||||||
|
|
||||||
|
vlm_host = LaunchConfiguration('vlm_host')
|
||||||
|
vlm_model = LaunchConfiguration('vlm_model')
|
||||||
|
image_topic = LaunchConfiguration('image_topic')
|
||||||
|
trigger_topic = LaunchConfiguration('trigger_topic')
|
||||||
|
trigger_sign = LaunchConfiguration('trigger_sign')
|
||||||
|
result_topic = LaunchConfiguration('result_topic')
|
||||||
|
prompt_text = LaunchConfiguration('prompt_text')
|
||||||
|
max_tokens = LaunchConfiguration('max_tokens')
|
||||||
|
image_max_dim = LaunchConfiguration('image_max_dim')
|
||||||
|
|
||||||
|
audio_sink = LaunchConfiguration('audio_sink')
|
||||||
|
tts_speed = LaunchConfiguration('tts_speed')
|
||||||
|
|
||||||
|
declare_use_vlm = DeclareLaunchArgument('use_vlm', default_value='true')
|
||||||
|
declare_use_tts = DeclareLaunchArgument('use_tts', default_value='true')
|
||||||
|
declare_use_qr_tts = DeclareLaunchArgument('use_qr_tts', default_value='false')
|
||||||
|
declare_config_file = DeclareLaunchArgument('config_file',
|
||||||
|
default_value=PathJoinSubstitution([
|
||||||
|
get_package_share_directory('vlm_detect'), 'config', 'vlm_detect.yaml']))
|
||||||
|
|
||||||
|
declare_vlm_host = DeclareLaunchArgument('vlm_host', default_value='http://192.168.175.111:8000')
|
||||||
|
declare_vlm_model = DeclareLaunchArgument('vlm_model', default_value='/home/wisdom/models/gguf/Qwen2-VL-2B-Instruct-Q4_K_M.gguf')
|
||||||
|
declare_image_topic = DeclareLaunchArgument('image_topic', default_value='/image')
|
||||||
|
declare_trigger_topic = DeclareLaunchArgument('trigger_topic', default_value='/sign4return')
|
||||||
|
declare_trigger_sign = DeclareLaunchArgument('trigger_sign', default_value='9')
|
||||||
|
declare_result_topic = DeclareLaunchArgument('result_topic', default_value='/vlm_result')
|
||||||
|
declare_prompt_text = DeclareLaunchArgument('prompt_text', default_value='图中是一个2D动漫插画风格的医院病房,有一个病人。请描述这个病人的状态。不要描述边框、背景、环境。20字以内。')
|
||||||
|
declare_max_tokens = DeclareLaunchArgument('max_tokens', default_value='100')
|
||||||
|
declare_image_max_dim = DeclareLaunchArgument('image_max_dim', default_value='128')
|
||||||
|
declare_audio_sink = DeclareLaunchArgument('audio_sink',
|
||||||
|
default_value='alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo')
|
||||||
|
declare_tts_speed = DeclareLaunchArgument('tts_speed', default_value='1.5')
|
||||||
|
|
||||||
|
vlm_node = Node(
|
||||||
|
package='vlm_detect',
|
||||||
|
executable='vlm_node',
|
||||||
|
name='vlm_detect',
|
||||||
|
output='screen',
|
||||||
|
condition=IfCondition(use_vlm),
|
||||||
|
parameters=[config_file,
|
||||||
|
{
|
||||||
|
'vlm_host': vlm_host,
|
||||||
|
'vlm_model': vlm_model,
|
||||||
|
'image_topic': image_topic,
|
||||||
|
'trigger_topic': trigger_topic,
|
||||||
|
'trigger_sign': trigger_sign,
|
||||||
|
'result_topic': result_topic,
|
||||||
|
'prompt_text': prompt_text,
|
||||||
|
'max_tokens': max_tokens,
|
||||||
|
'image_max_dim': image_max_dim,
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
|
||||||
|
tts_server = Node(
|
||||||
|
package='vlm_detect',
|
||||||
|
executable='tts_server',
|
||||||
|
name='tts_server',
|
||||||
|
output='screen',
|
||||||
|
condition=IfCondition(use_tts),
|
||||||
|
parameters=[config_file,
|
||||||
|
{
|
||||||
|
'audio_sink': audio_sink,
|
||||||
|
'tts_speed': tts_speed,
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
|
||||||
|
qr_tts_bridge = Node(
|
||||||
|
package='vlm_detect',
|
||||||
|
executable='qr_tts_bridge',
|
||||||
|
name='qr_tts_bridge',
|
||||||
|
output='screen',
|
||||||
|
condition=IfCondition(use_qr_tts),
|
||||||
|
)
|
||||||
|
|
||||||
|
return LaunchDescription([
|
||||||
|
declare_use_vlm,
|
||||||
|
declare_use_tts,
|
||||||
|
declare_use_qr_tts,
|
||||||
|
declare_config_file,
|
||||||
|
declare_vlm_host,
|
||||||
|
declare_vlm_model,
|
||||||
|
declare_image_topic,
|
||||||
|
declare_trigger_topic,
|
||||||
|
declare_trigger_sign,
|
||||||
|
declare_result_topic,
|
||||||
|
declare_prompt_text,
|
||||||
|
declare_max_tokens,
|
||||||
|
declare_image_max_dim,
|
||||||
|
declare_audio_sink,
|
||||||
|
declare_tts_speed,
|
||||||
|
LogInfo(msg=['Config: ', config_file]),
|
||||||
|
LogInfo(msg=['VLM Host: ', vlm_host]),
|
||||||
|
vlm_node,
|
||||||
|
tts_server,
|
||||||
|
qr_tts_bridge,
|
||||||
|
])
|
||||||
Reference in New Issue
Block a user