Files
car_nav_lite/scripts/test_goal.py
cyy 5156a8d766 MPPI planner + fence-line localization + scan obstacles
- Lightweight MPPI (200 trajectories, 15 steps, endpoint path_align)
- adaptive exploration (more random when stuck)
- fence-line Hough detection + geometric correction
- scan hit points marked at 255 immediately
- forward half-step pre-check for obstacle avoidance
- Ackermann constraint |wz| <= |vx|/min_turn_r
- XORshift RNG (stdlib broken on ARM)
2026-07-02 21:29:41 +00:00

88 lines
2.7 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Send a goal pose to car_nav_lite via /goal_pose topic.
Usage:
python3 test_goal.py # default goal
python3 test_goal.py --x 1.0 --y -1.5 # custom goal
python3 test_goal.py --auto # auto-cycle multiple goals
"""
import time
import argparse
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import PoseStamped
GOALS = [
# (x, y) — world coords in map frame (origin at center, 5m×5m)
# Car spawns near (-2.0, -2.3) facing +x
# Goal A: straight ahead ~3m, slight right
# Should trigger multi-pass inflation (cone at -1.58,-2.31 blocks straight path)
(0.98, -1.49),
# Goal B: far corner
(2.0, 2.0),
# Goal C: left side
(-1.5, 0.5),
]
class GoalSender(Node):
def __init__(self):
super().__init__("goal_sender")
self.pub = self.create_publisher(PoseStamped, "/goal_pose", 10)
self.idx = 0
def send(self, x, y, yaw=0.0):
msg = PoseStamped()
msg.header.frame_id = "map"
msg.header.stamp = self.get_clock().now().to_msg()
msg.pose.position.x = float(x)
msg.pose.position.y = float(y)
msg.pose.position.z = 0.0
# yaw encoded as quaternion (scalar-last: qx,qy,qz,qw)
msg.pose.orientation.z = float(__import__('math').sin(yaw / 2.0))
msg.pose.orientation.w = float(__import__('math').cos(yaw / 2.0))
self.pub.publish(msg)
self.get_logger().info(f"Sent goal: ({x:.2f}, {y:.2f}, yaw={yaw:.2f})")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--x", type=float, default=None)
parser.add_argument("--y", type=float, default=None)
parser.add_argument("--yaw", type=float, default=0.0)
parser.add_argument("--auto", action="store_true",
help="Auto-cycle through test goals")
parser.add_argument("--interval", type=float, default=30.0,
help="Seconds between auto goals")
args = parser.parse_args()
rclpy.init()
node = GoalSender()
if args.auto:
node.get_logger().info(f"Auto mode: {len(GOALS)} goals, {args.interval}s interval")
while rclpy.ok():
x, y = GOALS[node.idx % len(GOALS)]
node.send(x, y, 0.0)
node.idx += 1
time.sleep(args.interval)
elif args.x is not None and args.y is not None:
node.send(args.x, args.y, args.yaw)
else:
# Default: use first goal
x, y = GOALS[0]
node.get_logger().info(f"No args — sending default goal ({x:.2f}, {y:.2f})")
node.send(x, y, 0.0)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()