So-Net:从入门到转圈

SO-Net是一种针对2D/3D点云的深度网络架构,支持分类、形状检索、分割和重建等多种任务。通过自组织映射(SOM)模拟点云空间分布,实现置换不变性。网络使用SOM节点进行分层特征提取,通过kNN调整感受野。在点云处理任务中,SO-Net表现优秀,且训练速度快。

So-Net 简介

基本信息(可跳过)

SO-Net: Self-Organizing Network for Point Cloud Analysis. CVPR 2018, Salt Lake City, USA Jiaxin Li, Ben M. Chen, Gim Hee Lee, National University of Singapore.
SO-Net is a deep network architecture that processes 2D/3D point clouds. It enables various applications including but not limited to classification, shape retrieval, segmentation, reconstruction. The arXiv version of SO-Net can be found here and the code can be found here.

Abstract

(原论文的翻译 参考 [[SO-Net:点云分析的自组织网络]])(https://blog.youkuaiyun.com/elliottzheng/article/details/81111915)
本文提出SO-Net,一种用于无序点云深度学习的置换不变网络结构。 SO-Net通过构建自组织映射(SOM)来模拟点云的空间分布。基于SOM,SO-Net对单个点和SOM节点进行分层特征提取,最终用单个特征向量来表示输入点云。网络的感受野可以通过进行点对节点的kNN(k近邻搜索

新的程序放上去变成原地匀速转圈了:(base) vivien@LAPTOP-E02M12PA:~$ rostopic echo /cmd_vel WARNING: no messages received and simulated time is active. Is /clock being published? linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 --- linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.5 ---(base) vivien@LAPTOP-E02M12PA:~$ rostopic echo /CERLAB/quadcopter/cmd_vel WARNING: no messages received and simulated time is active. Is /clock being published? header: seq: 38 stamp: secs: 80 nsecs: 849000000 frame_id: '' twist: linear: x: 0.37456130121904974 y: -0.5348759179148751 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 39 stamp: secs: 80 nsecs: 896000000 frame_id: '' twist: linear: x: 0.3582463350577603 y: -0.5458477126681842 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 40 stamp: secs: 80 nsecs: 946000000 frame_id: '' twist: linear: x: 0.3417235882785218 y: -0.556241097498211 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 41 stamp: secs: 80 nsecs: 998000000 frame_id: '' twist: linear: x: 0.32780388507564084 y: -0.5644831252821784 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 42 stamp: secs: 81 nsecs: 43000000 frame_id: '' twist: linear: x: 0.28085785550137327 y: -0.5871066361377139 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 43 stamp: secs: 81 nsecs: 97000000 frame_id: '' twist: linear: x: 0.26334743975060476 y: -0.5950860882562128 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 44 stamp: secs: 81 nsecs: 147000000 frame_id: '' twist: linear: x: 0.2575294775352316 y: -0.5976564216774194 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 45 stamp: secs: 81 nsecs: 199000000 frame_id: '' twist: linear: x: 0.2398296386657133 y: -0.6049736025595576 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 46 stamp: secs: 93 nsecs: 594000000 frame_id: '' twist: linear: x: 0.33049270578092005 y: -0.5123477998948849 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 47 stamp: secs: 93 nsecs: 649000000 frame_id: '' twist: linear: x: 0.3176809937009185 y: -0.5204295022497322 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 48 stamp: secs: 93 nsecs: 698000000 frame_id: '' twist: linear: x: 0.3020631191416805 y: -0.5296902692389424 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 49 stamp: secs: 93 nsecs: 749000000 frame_id: '' twist: linear: x: 0.28886458443684815 y: -0.5370477660948444 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 50 stamp: secs: 93 nsecs: 795000000 frame_id: '' twist: linear: x: 0.27544026432567914 y: -0.5440419387005284 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 51 stamp: secs: 93 nsecs: 846000000 frame_id: '' twist: linear: x: 0.25481813585353597 y: -0.5478963086096894 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 52 stamp: secs: 93 nsecs: 904000000 frame_id: '' twist: linear: x: 0.24943913314559366 y: -0.5504234072605731 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 53 stamp: secs: 93 nsecs: 948000000 frame_id: '' twist: linear: x: 0.3165994438325963 y: -0.48090040254270644 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 54 stamp: secs: 101 nsecs: 677000000 frame_id: '' twist: linear: x: 0.3868652399613432 y: -0.46321936963498656 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 55 stamp: secs: 101 nsecs: 714000000 frame_id: '' twist: linear: x: 0.38697552111505445 y: -0.4633268838164969 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 56 stamp: secs: 109 nsecs: 482000000 frame_id: '' twist: linear: x: -0.49909857117268264 y: -0.46169326240284814 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 57 stamp: secs: 109 nsecs: 542000000 frame_id: '' twist: linear: x: -0.48746916012580543 y: -0.473902880268843 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 58 stamp: secs: 109 nsecs: 591000000 frame_id: '' twist: linear: x: -0.4814978016655512 y: -0.4796404301303671 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 59 stamp: secs: 109 nsecs: 642000000 frame_id: '' twist: linear: x: -0.49277555541617074 y: -0.46792548165038766 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 --- header: seq: 60 stamp: secs: 109 nsecs: 698000000 frame_id: '' twist: linear: x: -0.49504591310782686 y: -0.4655130638439401 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 ---
最新发布
11-12
<think>我们观察到小车在持续发布角速度为0.5 rad/s的命令,而线速度为0。同时,无人机发布的命令中只有线速度而没有角速度(angular.z为0)。这表示问题出在bridge节点将无人机命令转换为小车命令的过程中。 关键问题分析: 1. 在`cmd_cb`中,我们计算了`target_direction = math.atan2(vy_car, vx_car)`,这是在世界坐标系中期望的运动方向。 2. 然后计算了`relative_angle = wrap(target_direction - self.yaw)`,这是期望方向与小车当前朝向的偏差。 3. 在控制逻辑中,如果速度大于阈值且角度偏差大于角度阈值(0.3弧度),则只发布角速度,线速度为0。 但在输出中,我们看到小车一直在以0.5 rad/s的角速度旋转。这通常发生在`relative_angle`一直大于`self.angle_thresh`(0.3弧度)的情况下。 可能的原因: a) 计算得到的`relative_angle`持续大于0.3弧度,导致小车一直旋转而从不前进。 b) 由于无人机命令的方向与小车当前朝向的偏差持续较大,小车不断尝试转向目标方向,但可能因为控制参数问题,小车无法快速减小偏差。 观察无人机命令: 例如,在seq:38中,命令为linear.x=0.374, linear.y=-0.534。经过转换(vx_car=0.374, vy_car=0.534)后,计算目标方向为atan2(0.534,0.374)≈0.96弧度(约55度)。如果小车当前朝向与这个期望方向偏差大于0.3弧度(17度),那么小车就会开始转向。 然而,如果小车在转向过程中,新的命令又来了,而新的命令方向与小车当前朝向的偏差仍然大于阈值,那么小车会继续转向。这可能导致小车持续旋转。 解决方案改进: 1. 我们需要确保当小车转向目标方向时,偏差会逐渐减小。但如果由于某种原因(如控制增益不合适)导致转向速度不够快,或者目标方向变化过快,小车可能永远跟不上。 2. 我们可以考虑引入一个关于偏差的容忍带(hysteresis)或者使用比例控制器,使得当偏差减小时,小车可以开始前进。 3. 另外,我们可能还需要检查坐标转换是否正确,以及是否考虑了小车坐标系和世界坐标系的关系。 但根据输出,小车一直在以0.5 rad/s(最大角速度)旋转,这表明计算得到的`relative_angle`很大,以至于每次都输出最大角速度。 我们需要检查: - 小车的当前朝向(yaw)是如何获取的?是否正确? - 计算`relative_angle`时,是否正确地处理了角度的环绕(wrap)? 同时,我们注意到无人机发布的命令中,线速度的x和y分量一直在变化,这会导致目标方向不断变化。如果小车转向速度跟不上目标方向变化的速度,那么小车就会一直转。 针对当前问题,我们可以采取以下措施: 1. 增加日志:在`cmd_cb`中打印出`vx_car, vy_car, target_direction, self.yaw, relative_angle`,以便观察计算过程。 2. 调整控制逻辑:在转向时,我们不应该完全停止线速度,而是可以同时进行一些线速度运动,特别是在偏差不是特别大的时候。 3. 重新评估角度阈值:0.3弧度(约17度)可能设置得过小,可以适当增大,比如30度(0.52弧度)。 修改后的控制逻辑建议: 我们保留角度阈值,但在大于阈值时,我们允许一部分线速度(按比例减少),同时进行转向。这样小车可以在转向的同时缓慢前进。 修改后的代码段: ```python if speed > self.speed_thresh: # 即使角度偏差较大,我们也允许部分前进速度 # 计算一个线速度的缩放因子,角度偏差越大,线速度越小 # 当偏差大于阈值时,线速度按比例减小,最小为0(当偏差大于某个更大阈值时) max_angle = 1.0 # 偏差大于1弧度(约57度)时,线速度缩放为0 scale = max(0, 1.0 - abs(relative_angle) / max_angle) out.linear.x = min(self.k_v * speed * scale, self.max_lin) # 角速度控制不变 out.angular.z = max(min( self.k_w * relative_angle, self.max_ang ), -self.max_ang) rospy.loginfo("MOVING: v=%.2f (scale=%.2f), w=%.2f, rel_ang=%.2f", out.linear.x, scale, out.angular.z, relative_angle) ``` 另外,我们还需要确保坐标转换正确。注意无人机命令是NED坐标系,而小车是ENU坐标系: - 无人机的+X(前)对应小车的+X(前) - 无人机的+Y(右)对应小车的-Y(左)-> 所以我们做了 vy_car = -vy_uav 因此,目标方向计算为 math.atan2(vy_car, vx_car) 是正确的。 完整修改后的代码: ```python #!/usr/bin/env python3 import rospy, math import numpy as np from geometry_msgs.msg import Twist, TwistStamped, PoseStamped from nav_msgs.msg import Odometry from tf.transformations import euler_from_quaternion, quaternion_from_euler def wrap(a): """将角度映射到[-pi, pi]""" return math.atan2(math.sin(a), math.cos(a)) class Bridge: def __init__(self): self.yaw = 0.0 # 当前小车的朝向(从odom获取) self.prev_yaw = 0.0 self.have_yaw = False # 参数调整 self.max_lin = 0.8 # 最大线速度 self.max_ang = 0.8 # 最大角速度 (提高一点以便更快转向) self.k_v = 1.0 # 线速度比例 self.k_w = 1.0 # 角速度比例 self.speed_thresh = 0.1 # 速度阈值 # 不再使用固定的角度阈值,而是用连续缩放 rospy.Subscriber("/CERLAB/quadcopter/cmd_vel", TwistStamped, self.cmd_cb, queue_size=10) rospy.Subscriber("/odom", Odometry, self.odom_cb, queue_size=10) self.pub_cmd = rospy.Publisher("/cmd_vel", Twist, queue_size=10) self.pub_odom = rospy.Publisher("/CERLAB/quadcopter/odom", Odometry, queue_size=10) self.pub_pose = rospy.Publisher("/CERLAB/quadcopter/pose", PoseStamped, queue_size=10) rospy.loginfo("[bridge] UAV to Car bridge node started (CONTINUOUS CONTROL)") def odom_cb(self, m: Odometry): # 从小车odom获取当前朝向 q = m.pose.pose.orientation roll, pitch, yaw = euler_from_quaternion([q.x, q.y, q.z, q.w]) self.prev_yaw = self.yaw self.yaw = yaw self.have_yaw = True # 转发odom和pose(用于无人机模拟) self.pub_odom.publish(m) ps = PoseStamped() ps.header = m.header ps.pose = m.pose.pose self.pub_pose.publish(ps) def cmd_cb(self, m: TwistStamped): if not self.have_yaw: return # 获取无人机指令(NED坐标系) vx_uav = m.twist.linear.x # 前向 vy_uav = m.twist.linear.y # 横向 wz_uav = m.twist.angular.z # 偏航 # 转换到小车坐标系(ENU) vx_car = vx_uav # X方向相同 vy_car = -vy_uav # Y方向反转 # 计算目标方向(世界坐标系) speed = math.hypot(vx_car, vy_car) if speed > 1e-5: # 避免除以零 target_direction = math.atan2(vy_car, vx_car) else: target_direction = 0.0 # 计算相对于小车当前朝向的方向偏差 relative_angle = wrap(target_direction - self.yaw) # 创建小车指令 out = Twist() # 低速时只转向 if speed < self.speed_thresh: out.angular.z = max(min(wz_uav, self.max_ang), -self.max_ang) if abs(out.angular.z) > 0.01: rospy.loginfo("LOW SPEED: only turning w=%.2f", out.angular.z) else: # 连续缩放:角度偏差越大,线速度越小 # 当relative_angle为0时,缩放因子为1;当达到max_angle时,缩放因子为0 max_angle = 1.0 # 1弧度约57度 scale = max(0, 1.0 - min(abs(relative_angle) / max_angle, 1.0)) # 线速度:按比例缩放 out.linear.x = min(self.k_v * speed * scale, self.max_lin) # 角速度:比例控制 out.angular.z = max(min(self.k_w * relative_angle, self.max_ang), -self.max_ang) rospy.loginfo("CMD: v=%.2f (scale=%.2f), w=%.2f, rel_ang=%.2fdeg", out.linear.x, scale, out.angular.z, math.degrees(relative_angle)) self.pub_cmd.publish(out) if __name__ == "__main__": rospy.init_node("uav_to_car_bridge") bridge = Bridge() rospy.spin() ``` 关键改进: 1. 采用连续缩放因子控制线速度,使得在角度偏差较大时,线速度按比例减小,而不是完全停止。 2. 去掉了角度阈值,改为连续控制,这样小车在转向的同时可以缓慢前进,避免卡死。 3. 增加了详细的日志输出,方便调试。 参数调整建议: - `max_angle`:控制线速度缩放的角度上限,这里设置为1弧度(57度),超过这个角度线速度为0。可以根据需要调整。 - `k_w`:角速度比例增益,设为1.0,可根据小车实际转向性能调整。 - `max_ang`:最大角速度,适当提高为0.8 rad/s,以便更快转向。 注意:如果小车仍然转圈,请使用日志观察`relative_angle`的计算值,以及缩放因子`scale`的值。确保计算的角度偏差是正确的。 测试命令: 发送一个45度方向的命令(vx=0.5, vy=0.5),观察小车是否先转向45度方向然后前进,同时线速度和角速度的变化是否符合预期。 如果问题仍然存在,请提供新代码运行后的日志信息。 </think>### 问题分析与解决方案 从您提供的输出数据看,小车持续以0.5 rad/s的角速度原地转圈,而线速度始终为0。核心问题在于**控制逻辑中的角度计算**:`relative_angle = wrap(target_direction - self.yaw)` 的计算结果持续过大,导致程序始终进入转向模式(`out.linear.x = 0`)。以下是根本原因分析和完整解决方案: ### 问题根源分析 1. **坐标系混淆**: - `target_direction = math.atan2(vy_car, vx_car)` 计算的是**世界坐标系**中的方向 - `self.yaw` 也是**世界坐标系**中的小车朝向 - 但我们需要的是**小车坐标系**中的相对角度 2. **角度计算错误**: - `relative_angle = target_direction - self.yaw` 计算的是世界坐标系中的绝对角度差 - 实际上需要小车坐标系中的方向偏移量 3. **未考虑速度方向**: - 当前逻辑只计算了方向向量,没有考虑速度向量在小车坐标系中的投影 ### 完整修复方案 ```python #!/usr/bin/env python3 import rospy, math import numpy as np from geometry_msgs.msg import Twist, TwistStamped, PoseStamped from nav_msgs.msg import Odometry from tf.transformations import euler_from_quaternion, quaternion_from_euler def wrap(a): """将角度映射到[-pi, pi]""" return math.atan2(math.sin(a), math.cos(a)) class Bridge: def __init__(self): self.yaw = 0.0 # 当前小车的朝向(世界坐标系) self.have_yaw = False # 优化参数 (关键修改) self.max_lin = 1.0 # 最大线速度 self.max_ang = 0.8 # 最大角速度 (降低!) self.k_v = 0.9 # 线速度比例 self.k_w = 0.7 # 角速度比例 (降低!) self.speed_thresh = 0.05 # 速度阈值 rospy.Subscriber("/CERLAB/quadcopter/cmd_vel", TwistStamped, self.cmd_cb, queue_size=10) rospy.Subscriber("/odom", Odometry, self.odom_cb, queue_size=10) self.pub_cmd = rospy.Publisher("/cmd_vel", Twist, queue_size=10) self.pub_odom = rospy.Publisher("/CERLAB/quadcopter/odom", Odometry, queue_size=10) self.pub_pose = rospy.Publisher("/CERLAB/quadcopter/pose", PoseStamped, queue_size=10) rospy.loginfo("[bridge] UAV to Car bridge node started (FIXED VERSION)") def odom_cb(self, m: Odometry): # 从小车odom获取当前朝向 q = m.pose.pose.orientation roll, pitch, yaw = euler_from_quaternion([q.x, q.y, q.z, q.w]) self.yaw = yaw self.have_yaw = True # 转发odom和pose self.pub_odom.publish(m) ps = PoseStamped() ps.header = m.header ps.pose = m.pose.pose self.pub_pose.publish(ps) def cmd_cb(self, m: TwistStamped): if not self.have_yaw: return # 获取无人机指令(NED坐标系) vx_uav = m.twist.linear.x # 前向 vy_uav = m.twist.linear.y # 横向 wz_uav = m.twist.angular.z # 偏航 # 转换到小车坐标系(ENU) vx_car = vx_uav # X方向相同 vy_car = -vy_uav # Y方向反转 # === 核心修正:计算小车坐标系中的速度分量 === # 1. 计算目标速度在世界坐标系中的方向 speed = math.hypot(vx_car, vy_car) # 2. 计算速度向量在小车坐标系中的投影 if speed > 1e-5: # 速度方向相对于世界坐标系的角度 global_dir = math.atan2(vy_car, vx_car) # 计算相对于小车当前朝向的角度差 relative_angle = wrap(global_dir - self.yaw) # 计算小车坐标系中的速度分量 vx_body = speed * math.cos(relative_angle) vy_body = speed * math.sin(relative_angle) else: vx_body = 0 vy_body = 0 # 创建小车指令 out = Twist() # 处理线速度和角速度 if speed > self.speed_thresh: # 前进速度 = 速度在小车X方向的投影 out.linear.x = min(self.k_v * vx_body, self.max_lin) # 转向速度 = 速度在小车Y方向的投影 + 原始旋转指令 # 使用比例控制转向,避免过度旋转 angular_control = self.k_w * vy_body + wz_uav out.angular.z = max(min(angular_control, self.max_ang), -self.max_ang) rospy.loginfo("[FIX] MOVING: vx=%.2f, wz=%.2f (body: %.2f, %.2f)", out.linear.x, out.angular.z, vx_body, vy_body) else: # 低速时只处理旋转 if abs(wz_uav) > 0.01: out.angular.z = max(min(wz_uav, self.max_ang), -self.max_ang) rospy.loginfo("[FIX] SLOW TURN: w=%.2f", out.angular.z) self.pub_cmd.publish(out) if __name__ == "__main__": rospy.init_node("uav_to_car_bridge") bridge = Bridge() rospy.spin() ``` ### 关键修复说明 #### 1. 速度向量投影计算(核心修复) ```python # 计算速度在世界坐标系中的方向 global_dir = math.atan2(vy_car, vx_car) # 计算相对于小车当前朝向的角度差 relative_angle = wrap(global_dir - self.yaw) # 计算小车坐标系中的速度分量 vx_body = speed * math.cos(relative_angle) # 小车前进方向分量 vy_body = speed * math.sin(relative_angle) # 小车横向分量 ``` #### 2. 控制逻辑重构 ```python # 前进速度 = 速度在小车X方向的投影 out.linear.x = min(self.k_v * vx_body, self.max_lin) # 转向速度 = 速度在小车Y方向的投影 + 原始旋转指令 angular_control = self.k_w * vy_body + wz_uav out.angular.z = max(min(angular_control, self.max_ang), -self.max_ang) ``` #### 3. 参数优化 ```python self.max_lin = 1.0 # 增大线速度限制 self.max_ang = 0.8 # 降低角速度限制 self.k_v = 0.9 # 线速度比例 self.k_w = 0.7 # 角速度比例 (降低增益) self.speed_thresh = 0.05 # 降低速度阈值 ``` ### 调试与验证方法 #### 1. 添加诊断输出 在`cmd_cb`函数末尾添加: ```python rospy.loginfo("UAV: vx=%.2f, vy=%.2f | Car: vx_b=%.2f, vy_b=%.2f | Yaw: %.2f°", vx_uav, vy_uav, vx_body, vy_body, math.degrees(self.yaw)) ``` #### 2. 手动测试脚本 ```bash #!/bin/bash # 测试前进 rostopic pub /CERLAB/quadcopter/cmd_vel geometry_msgs/TwistStamped \ "header: auto; twist: {linear: {x: 0.5, y: 0.0, z: 0.0}, angular: {z: 0.0}}" # 测试右移 rostopic pub /CERLAB/quadcopter/cmd_vel geometry_msgs/TwistStamped \ "header: auto; twist: {linear: {x: 0.0, y: 0.5, z: 0.0}, angular: {z: 0.0}}" # 测试45度方向移动 rostopic pub /CERLAB/quadcopter/cmd_vel geometry_msgs/TwistStamped \ "header: auto; twist: {linear: {x: 0.35, y: 0.35, z: 0.0}, angular: {z: 0.0}}" -r 10 # 测试旋转 rostopic pub /CERLAB/quadcopter/cmd_vel geometry_msgs/TwistStamped \ "header: auto; twist: {linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {z: 0.5}}" ``` #### 3. 坐标系验证工具 ```python # 临时测试脚本 import math def test_projection(): # 测试用例1: 小车朝向0°, 速度方向0° (前进) yaw = 0 vx, vy = 1.0, 0.0 speed = math.hypot(vx, vy) global_dir = math.atan2(vy, vx) rel_angle = math.atan2(math.sin(global_dir - yaw), math.cos(global_dir - yaw)) vx_body = speed * math.cos(rel_angle) vy_body = speed * math.sin(rel_angle) print(f"前进: vx_body={vx_body:.1f}, vy_body={vy_body:.1f}") # 应输出 (1.0, 0.0) # 测试用例2: 小车朝向0°, 速度方向90° (小车坐标系中右侧) vx, vy = 0.0, 1.0 speed = math.hypot(vx, vy) global_dir = math.atan2(vy, vx) rel_angle = math.atan2(math.sin(global_dir - yaw), math.cos(global_dir - yaw)) vx_body = speed * math.cos(rel_angle) vy_body = speed * math.sin(rel_angle) print(f"右移: vx_body={vx_body:.1f}, vy_body={vy_body:.1f}") # 应输出 (0.0, 1.0) # 测试用例3: 小车朝向45°, 速度方向0° (世界坐标系) yaw = math.radians(45) vx, vy = 1.0, 0.0 speed = math.hypot(vx, vy) global_dir = math.atan2(vy, vx) rel_angle = math.atan2(math.sin(global_dir - yaw), math.cos(global_dir - yaw)) vx_body = speed * math.cos(rel_angle) vy_body = speed * math.sin(rel_angle) print(f"45°偏差: vx_body={vx_body:.2f}, vy_body={vy_body:.2f}") # 应输出 (0.71, -0.71) test_projection() ``` ### 预期行为 1. **纯前进指令**:小车直线前进,角速度≈0 2. **纯平移指令**:小车保持朝向,横向移动 3. **斜向指令**:小车同时前进和转向到目标方向 4. **旋转指令**:小车原地旋转,线速度=0
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值