fix: enable added week deletion and preserve learning concepts when cloning courses
This commit is contained in:
@@ -27,6 +27,7 @@ from nav_msgs.msg import OccupancyGrid, Odometry, Path as NavPath
|
||||
from nav2_msgs.action import NavigateToPose
|
||||
from pydantic import BaseModel
|
||||
from rclpy.action import ActionClient
|
||||
from rclpy.clock import Clock, ClockType
|
||||
from rclpy.executors import MultiThreadedExecutor
|
||||
from rclpy.node import Node
|
||||
from rclpy.parameter import Parameter
|
||||
@@ -132,6 +133,7 @@ def resolve_2d_transform(transforms: dict, target_frame: str, source_frame: str)
|
||||
class VelocityPayload(BaseModel):
|
||||
linear: float = 0.0
|
||||
angular: float = 0.0
|
||||
deadman: bool = False
|
||||
|
||||
|
||||
class SteeringPayload(BaseModel):
|
||||
@@ -257,6 +259,8 @@ class SlamNode(Node):
|
||||
if self.color_point_cloud_topic not in (self.point_cloud_topic, self.fallback_point_cloud_topic):
|
||||
self.create_subscription(PointCloud2, self.color_point_cloud_topic, self._on_color_point_cloud, qos_profile_sensor_data)
|
||||
self.create_subscription(CompressedImage, self.camera_image_topic, self._on_camera_image, qos_profile_sensor_data)
|
||||
if not os.environ.get("CAMERA_IMAGE_TOPIC"):
|
||||
self.create_subscription(CompressedImage, "/image_raw/compressed", self._on_camera_image, qos_profile_sensor_data)
|
||||
self.create_subscription(Odometry, self.icp_odom_topic, self._on_icp_odom, best_effort_qos)
|
||||
self.create_subscription(Odometry, self.icp_odom_topic, self._on_icp_odom, reliable_qos)
|
||||
self.create_subscription(OccupancyGrid, self.map_topic, self._on_map, map_qos)
|
||||
@@ -268,6 +272,10 @@ class SlamNode(Node):
|
||||
self.create_subscription(TFMessage, "/tf", self._on_tf, qos_profile_sensor_data)
|
||||
self.create_subscription(TFMessage, "/tf_static", self._on_tf, tf_static_qos)
|
||||
self._cmd_vel_pub = self.create_publisher(Twist, self.cmd_vel_topic, 10)
|
||||
self._teleop_lock = threading.Lock()
|
||||
self._teleop_deadline = None
|
||||
self._teleop_timer = self.create_timer(
|
||||
0.1, self._check_teleop_timeout, clock=Clock(clock_type=ClockType.STEADY_TIME))
|
||||
self._steering_pub = self.create_publisher(Float64, self.steering_topic, 10)
|
||||
self._initial_pose_pub = self.create_publisher(PoseWithCovarianceStamped, "/initialpose", 10)
|
||||
self._navigate_to_pose_client = ActionClient(self, NavigateToPose, self.navigate_to_pose_action)
|
||||
@@ -817,13 +825,27 @@ class SlamNode(Node):
|
||||
}
|
||||
self._tf_count += 1
|
||||
|
||||
def publish_cmd_vel(self, linear: float, angular: float):
|
||||
def publish_cmd_vel(self, linear: float, angular: float, deadman: bool = False):
|
||||
if self.read_only:
|
||||
raise RuntimeError("읽기 전용 3D SLAM 화면에서는 차량 제어를 지원하지 않습니다.")
|
||||
if not math.isfinite(linear) or not math.isfinite(angular):
|
||||
raise RuntimeError("Invalid velocity")
|
||||
if deadman:
|
||||
linear = max(-0.1, min(0.1, linear))
|
||||
angular = max(-0.5, min(0.5, angular))
|
||||
msg = Twist()
|
||||
msg.linear.x = float(linear)
|
||||
msg.angular.z = float(angular)
|
||||
self._cmd_vel_pub.publish(msg)
|
||||
with self._teleop_lock:
|
||||
self._teleop_deadline = time.monotonic() + 0.5 if deadman and (linear or angular) else None
|
||||
self._cmd_vel_pub.publish(msg)
|
||||
|
||||
def _check_teleop_timeout(self):
|
||||
# Only stop expired manual commands, never idle autonomous navigation.
|
||||
with self._teleop_lock:
|
||||
if self._teleop_deadline is not None and time.monotonic() >= self._teleop_deadline:
|
||||
self._teleop_deadline = None
|
||||
self._cmd_vel_pub.publish(Twist())
|
||||
|
||||
def set_simulator_steering(self, angle: float) -> None:
|
||||
if self.read_only:
|
||||
@@ -1192,6 +1214,7 @@ class SlamNode(Node):
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"teleop_deadman": not self.read_only,
|
||||
"topics": {
|
||||
"odom": self.odom_topic,
|
||||
"scan": self.scan_topic,
|
||||
@@ -1549,7 +1572,7 @@ def debug_topics():
|
||||
def cmd_vel(payload: VelocityPayload):
|
||||
if not slam_node:
|
||||
raise HTTPException(status_code=503, detail="ROS node is not ready")
|
||||
slam_node.publish_cmd_vel(payload.linear, payload.angular)
|
||||
slam_node.publish_cmd_vel(payload.linear, payload.angular, payload.deadman)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@@ -1643,11 +1666,12 @@ async def ws_control(websocket: WebSocket):
|
||||
velocity = VelocityPayload(
|
||||
linear=payload.get("linear", 0.0),
|
||||
angular=payload.get("angular", 0.0),
|
||||
deadman=payload.get("deadman", False),
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
try:
|
||||
slam_node.publish_cmd_vel(velocity.linear, velocity.angular)
|
||||
slam_node.publish_cmd_vel(velocity.linear, velocity.angular, velocity.deadman)
|
||||
except RuntimeError:
|
||||
continue
|
||||
except WebSocketDisconnect:
|
||||
|
||||
Reference in New Issue
Block a user