LitchiCheng

  • 2025-03-18
  • 发表了主题帖: ROS2 Moveit2 手把手教你 Panda 机械臂笛卡尔速度控制!(附代码)

        视频讲解: 代码仓库:GitHub - LitchiCheng/ros2_package 新建package,命名moveit_servo_panda ros2 pkg create --build-type ament_cmake moveit_servo_panda --dependencies rclcpp control_msgs geometry_msgs moveit_servo moveit_core moveit_msgs planning_scene_monitor tf2_ros CMakeLists.txt文件 cmake_minimum_required(VERSION 3.8) project(moveit_servo_panda) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() # find dependencies find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(control_msgs REQUIRED) find_package(geometry_msgs REQUIRED) find_package(moveit_servo REQUIRED) find_package(moveit_core REQUIRED) find_package(moveit_msgs REQUIRED) find_package(tf2_ros REQUIRED) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # comment the line when a copyright and license is added to all source files set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # comment the line when this package is in a git repo and when # a copyright and license is added to all source files set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() add_executable(panda_servo_node src/panda_servo.cpp) ament_target_dependencies(panda_servo_node rclcpp control_msgs geometry_msgs moveit_servo moveit_core moveit_msgs tf2_ros ) install(TARGETS panda_servo_node DESTINATION lib/${PROJECT_NAME} ) ament_package() src中新建panda_servo.cpp #include <rclcpp/rclcpp.hpp> #include <moveit_servo/servo_parameters.h> #include <moveit_servo/servo.h> #include <moveit/planning_scene_monitor/planning_scene_monitor.h> using namespace std::chrono_literals; static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_servo_panda.panda_servo_node"); rclcpp::Node::SharedPtr node_; rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr twist_cmd_pub_; double linear_x = 0.0; double linear_y = 0.0; double linear_z = 0.0; double angular_x = 0.0; double angular_y = 0.0; double angular_z = 0.0; void publishCommands() { auto msg = std::make_unique<geometry_msgs::msg::TwistStamped>(); msg->header.stamp = node_->now(); msg->header.frame_id = "panda_link0"; msg->twist.linear.x = linear_x; msg->twist.linear.y = linear_y; msg->twist.linear.z = linear_z; msg->twist.angular.x = angular_x; msg->twist.angular.y = angular_y; msg->twist.angular.z = angular_z; twist_cmd_pub_->publish(std::move(msg)); } int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::NodeOptions node_options; node_options.use_intra_process_comms(false); node_ = std::make_shared<rclcpp::Node>("panda_servo_node", node_options); node_->declare_parameter("linear_x", linear_x); node_->declare_parameter("linear_y", linear_y); node_->declare_parameter("linear_z", linear_z); node_->declare_parameter("angular_x", angular_x); node_->declare_parameter("angular_y", angular_y); node_->declare_parameter("angular_z", angular_z); node_->get_parameter("linear_x", linear_x); node_->get_parameter("linear_y", linear_y); node_->get_parameter("linear_z", linear_z); node_->get_parameter("angular_x", angular_x); node_->get_parameter("angular_y", angular_y); node_->get_parameter("angular_z", angular_z); auto tf_buffer = std::make_shared<tf2_ros::Buffer>(node_->get_clock()); auto planning_scene_monitor = std::make_shared<planning_scene_monitor::PlanningSceneMonitor>( node_, "robot_description", tf_buffer, "planning_scene_monitor"); if (planning_scene_monitor->getPlanningScene()) { planning_scene_monitor->startStateMonitor("/joint_states"); planning_scene_monitor->setPlanningScenePublishingFrequency(25); planning_scene_monitor->startPublishingPlanningScene(planning_scene_monitor::PlanningSceneMonitor::UPDATE_SCENE, "/moveit_servo/publish_planning_scene"); planning_scene_monitor->startSceneMonitor(); planning_scene_monitor->providePlanningSceneService(); } else { RCLCPP_ERROR(LOGGER, "Planning scene not configured"); return EXIT_FAILURE; } twist_cmd_pub_ = node_->create_publisher<geometry_msgs::msg::TwistStamped>("panda_servo_node/delta_twist_cmds", 10); auto servo_parameters = moveit_servo::ServoParameters::makeServoParameters(node_); if (!servo_parameters) { RCLCPP_FATAL(LOGGER, "Failed to load the servo parameters"); return EXIT_FAILURE; } auto servo = std::make_unique<moveit_servo::Servo>(node_, servo_parameters, planning_scene_monitor); servo->start(); rclcpp::TimerBase::SharedPtr timer = node_->create_wall_timer(50ms, publishCommands); auto executor = std::make_unique<rclcpp::executors::MultiThreadedExecutor>(); executor->add_node(node_); executor->spin(); rclcpp::shutdown(); return 0; } 编译运行,先运行rviz场景,再运动panda_servo,指定linear.x和angular.z colcon build --packages-select moveit_servo_panda source install/setup.bash ros2 launch moveit2_tutorials demo.launch.py rviz_config:=panda_moveit_config_demo_empty.rviz ros2 run moveit_servo_panda panda_servo_node --ros-args -p linear_x:=0.5 -p angular_z:=0.3     不动的原因可能是到达关节限位      

  • 回复了主题帖: DQN 玩 2048 实战|第二期!设计 ε 贪心策略神经网络,简单训练一下吧!

    Jacktang 发表于 2025-3-18 07:33 这设计 ε 贪心策略神经网络还是有点复杂啊 这还不算复杂,神经网络就是偷懒

  • 2025-03-17
  • 回复了主题帖: 别慌!WSL2 编译跳出 “Killed signal terminated program cc1plus” 的应对法

    freebsder 发表于 2025-3-17 15:12 我一直以为wsl用的是windows的系统内存 一样的

  • 发表了主题帖: DQN 玩 2048 实战|第三期!优化网络,使用GPU、Env奖励优化

        视频讲解:   1. 仅考虑局部合并奖励:目前的奖励只设置为合并方块时获得的分数,只关注了每一步的即时合并收益,而没有对最终达成 2048 这个目标给予额外的激励,如果没有对达成 2048 给予足够的奖励信号,Agent 可能不会将其作为一个重要的目标 2. 训练硬件资源利用不高,没有使用GPU进行加速,默认为CPU,较慢 代码修改如下: step函数里面,输入维度增加max_tile最大的数是多少 if 2048 in self.board: reward += 10000 done = True state = self.board.flatten() max_tile = np.max(self.board) state = np.append(state, max_tile) return state, reward, done input_size = 17 检查系统中是否有可用的 GPU,如果有则使用 GPU 进行计算,否则使用 CPU。 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 在 train ,创建模型实例后,使用 .to(device) 将模型移动到指定的设备(GPU 或 CPU) model = DQN(input_size, output_size).to(device) target_model = DQN(input_size, output_size).to(device) 在训练和推理过程中,将输入数据(状态、动作、奖励等)也移动到指定的设备上。 state = torch.FloatTensor(state).unsqueeze(0).to(device) next_state = torch.FloatTensor(next_state).unsqueeze(0).to(device) states = torch.FloatTensor(states).to(device) actions = torch.LongTensor(actions).to(device) rewards = torch.FloatTensor(rewards).to(device) next_states = torch.FloatTensor(next_states).to(device) dones = torch.FloatTensor(dones).to(device) 将 state 和 next_state 先使用 .cpu() 方法移动到 CPU 上,再使用 .numpy() 方法转换为 NumPy 数组 replay_buffer.add(state.cpu().squeeze(0).numpy(), action, reward, next_state.cpu().squeeze(0).numpy(), done) 这个不改的话,会出现 TypeError: can't convert cuda:0 device type tensor to numpy 错误 完整代码如下: import numpy as np import torch import torch.nn as nn import torch.optim as optim import random from collections import deque import matplotlib.pyplot as plt import matplotlib.colors as mcolors from matplotlib.table import Table device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 2048 游戏环境类 class Game2048: def __init__(self): self.board = np.zeros((4, 4), dtype=int) self.add_random_tile() self.add_random_tile() def add_random_tile(self): empty_cells = np.argwhere(self.board == 0) if len(empty_cells) > 0: index = random.choice(empty_cells) self.board[index[0], index[1]] = 2 if random.random() < 0.9 else 4 def move_left(self): reward = 0 new_board = np.copy(self.board) for row in range(4): line = new_board[row] non_zero = line[line != 0] merged = [] i = 0 while i < len(non_zero): if i + 1 < len(non_zero) and non_zero == non_zero[i + 1]: merged.append(2 * non_zero) reward += 2 * non_zero i += 2 else: merged.append(non_zero) i += 1 new_board[row] = np.pad(merged, (0, 4 - len(merged)), 'constant') if not np.array_equal(new_board, self.board): self.board = new_board self.add_random_tile() return reward def move_right(self): self.board = np.fliplr(self.board) reward = self.move_left() self.board = np.fliplr(self.board) return reward def move_up(self): self.board = self.board.T reward = self.move_left() self.board = self.board.T return reward def move_down(self): self.board = self.board.T reward = self.move_right() self.board = self.board.T return reward def step(self, action): if action == 0: reward = self.move_left() elif action == 1: reward = self.move_right() elif action == 2: reward = self.move_up() elif action == 3: reward = self.move_down() done = not np.any(self.board == 0) and all([ np.all(self.board[:, i] != self.board[:, i + 1]) for i in range(3) ]) and all([ np.all(self.board[i, :] != self.board[i + 1, :]) for i in range(3) ]) if 2048 in self.board: reward += 10000 done = True state = self.board.flatten() max_tile = np.max(self.board) state = np.append(state, max_tile) return state, reward, done def reset(self): self.board = np.zeros((4, 4), dtype=int) self.add_random_tile() self.add_random_tile() state = self.board.flatten() max_tile = np.max(self.board) state = np.append(state, max_tile) return state # 深度 Q 网络类 class DQN(nn.Module): def __init__(self, input_size, output_size): super(DQN, self).__init__() self.fc1 = nn.Linear(input_size, 128) self.fc2 = nn.Linear(128, 128) self.fc3 = nn.Linear(128, output_size) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) return self.fc3(x) # 经验回放缓冲区类 class ReplayBuffer: def __init__(self, capacity): self.buffer = deque(maxlen=capacity) def add(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): batch = random.sample(self.buffer, batch_size) states, actions, rewards, next_states, dones = zip(*batch) return np.array(states), np.array(actions), np.array(rewards), np.array(next_states), np.array(dones) def __len__(self): return len(self.buffer) # 可视化函数 def visualize_board(board, ax): ax.clear() table = Table(ax, bbox=[0, 0, 1, 1]) nrows, ncols = board.shape width, height = 1.0 / ncols, 1.0 / nrows # 定义颜色映射 cmap = mcolors.LinearSegmentedColormap.from_list("", ["white", "yellow", "orange", "red"]) for (i, j), val in np.ndenumerate(board): color = cmap(np.log2(val + 1) / np.log2(2048 + 1)) if val > 0 else "white" table.add_cell(i, j, width, height, text=val if val > 0 else "", loc='center', facecolor=color) ax.add_table(table) ax.set_axis_off() plt.draw() plt.pause(0.1) # 训练函数 def train(): env = Game2048() input_size = 17 output_size = 4 model = DQN(input_size, output_size).to(device) target_model = DQN(input_size, output_size).to(device) target_model.load_state_dict(model.state_dict()) target_model.eval() optimizer = optim.Adam(model.parameters(), lr=0.001) criterion = nn.MSELoss() replay_buffer = ReplayBuffer(capacity=10000) batch_size = 32 gamma = 0.99 epsilon = 1.0 epsilon_decay = 0.995 epsilon_min = 0.01 update_target_freq = 10 num_episodes = 1000 fig, ax = plt.subplots() for episode in range(num_episodes): state = env.reset() state = torch.FloatTensor(state).unsqueeze(0).to(device) done = False total_reward = 0 while not done: visualize_board(env.board, ax) if random.random() < epsilon: action = random.randint(0, output_size - 1) else: q_values = model(state) action = torch.argmax(q_values, dim=1).item() next_state, reward, done = env.step(action) next_state = torch.FloatTensor(next_state).unsqueeze(0).to(device) replay_buffer.add(state.cpu().squeeze(0).numpy(), action, reward, next_state.cpu().squeeze(0).numpy(), done) if len(replay_buffer) >= batch_size: states, actions, rewards, next_states, dones = replay_buffer.sample(batch_size) states = torch.FloatTensor(states).to(device) actions = torch.LongTensor(actions).to(device) rewards = torch.FloatTensor(rewards).to(device) next_states = torch.FloatTensor(next_states).to(device) dones = torch.FloatTensor(dones).to(device) q_values = model(states) q_values = q_values.gather(1, actions.unsqueeze(1)).squeeze(1) next_q_values = target_model(next_states) next_q_values = next_q_values.max(1)[0] target_q_values = rewards + gamma * (1 - dones) * next_q_values loss = criterion(q_values, target_q_values) optimizer.zero_grad() loss.backward() optimizer.step() state = next_state total_reward += reward if episode % update_target_freq == 0: target_model.load_state_dict(model.state_dict()) epsilon = max(epsilon * epsilon_decay, epsilon_min) print(f"Episode {episode}: Total Reward = {total_reward}, Epsilon = {epsilon}") plt.close() if __name__ == "__main__": train()      

  • 回复了主题帖: DQN 玩 2048 实战|第一期!搭建游戏环境(附 PyGame 可视化源码)

    Jacktang 发表于 2025-3-17 07:49 玩家获取胜利的条件是获得数值为2048的方块,这个说的是比较容易 是的,游戏规则不算难的

  • 2025-03-16
  • 回复了主题帖: Moveit2还真有python接口版本?带你使用关节位置控制(有小坑需要避免哦)!

    Jacktang 发表于 2025-3-16 09:18 panda的控制台报错了,找不到arm这个group, 这里需要做一点修改,修改的主要内容是什么呢 哈哈哈,修改的内容就是你没仔细看文章咯

  • 发表了主题帖: DQN 玩 2048 实战|第二期!设计 ε 贪心策略神经网络,简单训练一下吧!

        视频链接: 代码仓库:LitchiCheng/DRL-learning: 深度强化学习 概念介绍: DQN(深度 Q 网络,Deep Q-Network)中,Q 的全称是 “Quality”(质量),对应的完整术语是“状态 - 动作值函数”(State-Action Value Function),记作 Q(s,a) 定义:Q(s,a) 表示在状态 s 下执行动作 a 后,智能体未来累积奖励的期望(即 “长期收益的质量”)。 作用: Q 值是强化学习中 “决策” 的核心依据。智能体通过比较当前状态下所有可能动作的 Q 值,选择 Q 值最大的动作(即 “最优动作”),以最大化累积奖励。 网络设计有三点: 深度 Q 网络定义:使用 PyTorch 定义一个神经网络,用于近似 Q 值函数。 经验回放机制:实现经验回放缓冲区,用于存储智能体的经验,并随机采样进行训练。 使用 Epsilon-greedy 策略,是一种平衡探索(Exploration)与利用(Exploitation)的经典策略,核心解决 “如何避免智能体只依赖已知最优动作,而错过潜在更好策略” 的问题。 下面是代码 import numpy as np import torch import torch.nn as nn import torch.optim as optim import random from collections import deque import matplotlib.pyplot as plt import matplotlib.colors as mcolors from matplotlib.table import Table # 2048 游戏环境类 class Game2048: def __init__(self): self.board = np.zeros((4, 4), dtype=int) self.add_random_tile() self.add_random_tile() def add_random_tile(self): empty_cells = np.argwhere(self.board == 0) if len(empty_cells) > 0: index = random.choice(empty_cells) self.board[index[0], index[1]] = 2 if random.random() < 0.9 else 4 def move_left(self): reward = 0 new_board = np.copy(self.board) for row in range(4): line = new_board[row] non_zero = line[line != 0] merged = [] i = 0 while i < len(non_zero): if i + 1 < len(non_zero) and non_zero[i] == non_zero[i + 1]: merged.append(2 * non_zero[i]) reward += 2 * non_zero[i] i += 2 else: merged.append(non_zero[i]) i += 1 new_board[row] = np.pad(merged, (0, 4 - len(merged)), 'constant') if not np.array_equal(new_board, self.board): self.board = new_board self.add_random_tile() return reward def move_right(self): self.board = np.fliplr(self.board) reward = self.move_left() self.board = np.fliplr(self.board) return reward def move_up(self): self.board = self.board.T reward = self.move_left() self.board = self.board.T return reward def move_down(self): self.board = self.board.T reward = self.move_right() self.board = self.board.T return reward def step(self, action): if action == 0: reward = self.move_left() elif action == 1: reward = self.move_right() elif action == 2: reward = self.move_up() elif action == 3: reward = self.move_down() done = not np.any(self.board == 0) and all([ np.all(self.board[:, i] != self.board[:, i + 1]) for i in range(3) ]) and all([ np.all(self.board[i, :] != self.board[i + 1, :]) for i in range(3) ]) state = self.board.flatten() return state, reward, done def reset(self): self.board = np.zeros((4, 4), dtype=int) self.add_random_tile() self.add_random_tile() return self.board.flatten() # 深度 Q 网络类 class DQN(nn.Module): def __init__(self, input_size, output_size): super(DQN, self).__init__() self.fc1 = nn.Linear(input_size, 128) self.fc2 = nn.Linear(128, 128) self.fc3 = nn.Linear(128, output_size) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) return self.fc3(x) # 经验回放缓冲区类 class ReplayBuffer: def __init__(self, capacity): self.buffer = deque(maxlen=capacity) def add(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): batch = random.sample(self.buffer, batch_size) states, actions, rewards, next_states, dones = zip(*batch) return np.array(states), np.array(actions), np.array(rewards), np.array(next_states), np.array(dones) def __len__(self): return len(self.buffer) # 可视化函数 def visualize_board(board, ax): ax.clear() table = Table(ax, bbox=[0, 0, 1, 1]) nrows, ncols = board.shape width, height = 1.0 / ncols, 1.0 / nrows # 定义颜色映射 cmap = mcolors.LinearSegmentedColormap.from_list("", ["white", "yellow", "orange", "red"]) for (i, j), val in np.ndenumerate(board): color = cmap(np.log2(val + 1) / np.log2(2048 + 1)) if val > 0 else "white" table.add_cell(i, j, width, height, text=val if val > 0 else "", loc='center', facecolor=color) ax.add_table(table) ax.set_axis_off() plt.draw() plt.pause(0.1) # 训练函数 def train(): env = Game2048() input_size = 16 output_size = 4 model = DQN(input_size, output_size) target_model = DQN(input_size, output_size) target_model.load_state_dict(model.state_dict()) target_model.eval() optimizer = optim.Adam(model.parameters(), lr=0.001) criterion = nn.MSELoss() replay_buffer = ReplayBuffer(capacity=10000) batch_size = 32 gamma = 0.99 epsilon = 1.0 epsilon_decay = 0.995 epsilon_min = 0.01 update_target_freq = 10 num_episodes = 1000 fig, ax = plt.subplots() for episode in range(num_episodes): state = env.reset() state = torch.FloatTensor(state).unsqueeze(0) done = False total_reward = 0 while not done: visualize_board(env.board, ax) if random.random() < epsilon: action = random.randint(0, output_size - 1) else: q_values = model(state) action = torch.argmax(q_values, dim=1).item() next_state, reward, done = env.step(action) next_state = torch.FloatTensor(next_state).unsqueeze(0) replay_buffer.add(state.squeeze(0).numpy(), action, reward, next_state.squeeze(0).numpy(), done) if len(replay_buffer) >= batch_size: states, actions, rewards, next_states, dones = replay_buffer.sample(batch_size) states = torch.FloatTensor(states) actions = torch.LongTensor(actions) rewards = torch.FloatTensor(rewards) next_states = torch.FloatTensor(next_states) dones = torch.FloatTensor(dones) q_values = model(states) # 得到每个状态下实际采取动作的 Q 值 q_values = q_values.gather(1, actions.unsqueeze(1)).squeeze(1) next_q_values = target_model(next_states) # 得到下一个状态下最大的 Q 值 next_q_values = next_q_values.max(1)[0] # 目标 Q 值 target_q_values = rewards + gamma * (1 - dones) * next_q_values loss = criterion(q_values, target_q_values) optimizer.zero_grad() loss.backward() optimizer.step() state = next_state total_reward += reward if episode % update_target_freq == 0: target_model.load_state_dict(model.state_dict()) epsilon = max(epsilon * epsilon_decay, epsilon_min) print(f"Episode {episode}: Total Reward = {total_reward}, Epsilon = {epsilon}") plt.close() if __name__ == "__main__": train() 运行,会出现matplotlib可视化的2048操作过程,控制台输出当前训练的轮数等信息        

  • 2025-03-15
  • 发表了主题帖: DQN 玩 2048 实战|第一期!搭建游戏环境(附 PyGame 可视化源码)

        视频讲解: 代码仓库:GitHub - LitchiCheng/DRL-learning: 深度强化学习 2048游戏介绍,引用维基百科     《2048》在4×4的网格上进行。玩家可以使用上、下、左、右四个方向键移动所有方块。[1]但在部分情形下,不可向某些方向移动。[2]:66游戏开始时,网格上会出现两个数值为2或4的方块。每次移动后,另一个数值为2或4的新方块会随机出现在空方格上。[2]:66方块会沿着指定的方向滑动,直到被其它方块或网格边缘阻挡。如果两个相同数值的方块碰撞,它们将合并成一个方块,其数值等于两个方块的数值之和。[3][4]如果三个数值相同的方块碰撞,则只会合并靠近终点方向的两个方块,距起点最近的方块的数值不变。若一行或一列中的方块数值均相同,则沿着该行或该列滑动会合并前两个和后两个方块。[5]在同一移动过程中,新生成的方块不能再与其他方块合并。[6]数值较高的方块会发出柔和的光芒;但随着得分增加,光芒会不断变暗。[6]方块数值都是2的幂,最大为131072。[7]界面右上方的记分牌会记录玩家的分数。玩家的初始分数为零,每当两个方块合并时,分数会增加,得分取决于合并后方块的数值。[8] 玩家获取胜利的条件是获得数值为2048的方块。达到这一目标后,玩家可以继续游戏,以获得更高的分数。[9][10][11]当玩家没有合法的移动方法时,即出现了网格上没有空方格,且相邻方块的数值均不相同的情况,游戏就直接结束。[6][12] 使用pygame创建一个交互环境,实际作为env使用的时候可以不用pygame,换成matplotlib只做一个简单的显示即可,安装pygame pip3 install pygame -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 创建一个Env2048Gui.py import numpy as np import random import pygame # 2048 游戏环境类 class Game2048: def __init__(self): self.board = np.zeros((4, 4), dtype=int) self.add_random_tile() self.add_random_tile() def add_random_tile(self): empty_cells = np.argwhere(self.board == 0) if len(empty_cells) > 0: index = random.choice(empty_cells) self.board[index[0], index[1]] = 2 if random.random() < 0.9 else 4 def move_left(self): reward = 0 new_board = np.copy(self.board) for row in range(4): line = new_board[row] non_zero = line[line != 0] merged = [] i = 0 while i < len(non_zero): if i + 1 < len(non_zero) and non_zero[i] == non_zero[i + 1]: merged.append(2 * non_zero[i]) reward += 2 * non_zero[i] i += 2 else: merged.append(non_zero[i]) i += 1 new_board[row] = np.pad(merged, (0, 4 - len(merged)), 'constant') if not np.array_equal(new_board, self.board): self.board = new_board self.add_random_tile() return reward def move_right(self): self.board = np.fliplr(self.board) reward = self.move_left() self.board = np.fliplr(self.board) return reward def move_up(self): self.board = self.board.T reward = self.move_left() self.board = self.board.T return reward def move_down(self): self.board = self.board.T reward = self.move_right() self.board = self.board.T return reward def step(self, action): if action == 0: reward = self.move_left() elif action == 1: reward = self.move_right() elif action == 2: reward = self.move_up() elif action == 3: reward = self.move_down() done = not np.any(self.board == 0) and all([ np.all(self.board[:, i] != self.board[:, i + 1]) for i in range(3) ]) and all([ np.all(self.board[i, :] != self.board[i + 1, :]) for i in range(3) ]) state = self.board.flatten() return state, reward, done def reset(self): self.board = np.zeros((4, 4), dtype=int) self.add_random_tile() self.add_random_tile() return self.board.flatten() # 颜色定义 COLORS = { 0: (205, 193, 180), 2: (238, 228, 218), 4: (237, 224, 200), 8: (242, 177, 121), 16: (245, 149, 99), 32: (246, 124, 95), 64: (246, 94, 59), 128: (237, 207, 114), 256: (237, 204, 97), 512: (237, 200, 80), 1024: (237, 197, 63), 2048: (237, 194, 46) } # 绘制游戏板 def draw_board(screen, board, tile_size, margin): for i in range(4): for j in range(4): value = board[i][j] color = COLORS.get(value, (0, 0, 0)) pygame.draw.rect(screen, color, (j * (tile_size + margin) + margin, i * (tile_size + margin) + margin, tile_size, tile_size)) if value != 0: font = pygame.font.Font(None, 36) text = font.render(str(value), True, (0, 0, 0)) text_rect = text.get_rect(center=( j * (tile_size + margin) + margin + tile_size // 2, i * (tile_size + margin) + margin + tile_size // 2 )) screen.blit(text, text_rect) # 主函数 def main(): pygame.init() tile_size = 100 margin = 10 width = height = 4 * (tile_size + margin) + margin screen = pygame.display.set_mode((width, height)) pygame.display.set_caption("2048 Game") game = Game2048() done = False clock = pygame.time.Clock() while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: done = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: _, _, done = game.step(0) elif event.key == pygame.K_RIGHT: _, _, done = game.step(1) elif event.key == pygame.K_UP: _, _, done = game.step(2) elif event.key == pygame.K_DOWN: _, _, done = game.step(3) screen.fill((187, 173, 160)) draw_board(screen, game.board, tile_size, margin) pygame.display.flip() clock.tick(60) pygame.quit() if __name__ == "__main__": main() 运行后,可以通过上下左右键进行控制 python3 Env2048Gui.py      

  • 2025-03-14
  • 发表了主题帖: Moveit2还真有python接口版本?带你使用关节位置控制(有小坑需要避免哦)!

        视频讲解: 最近一个视频中小伙伴评论说moveit2没有python的接口,翻了一下,还真有,如下,库封装的很简单,操作说明也很简单,来复现一下,等下会讲下过程中的小细节需要处理的,不然复现是不成功的。 仓库地址:git@github.com:AndrejOrsula/pymoveit2.git(别忘了给人家点星,也别忘了给我点个) cd ~/ws_moveit2/src git clone git@github.com:AndrejOrsula/pymoveit2.git clone后的文件夹如下     colcon编译的方式改成我这个,不参考github上的 rosdep install -y -r -i --rosdistro ${ROS_DISTRO} --from-paths . colcon build --packages-select pymoveit2 source下setup.bash,使用一下 先打开panda机械臂场景 source install/setup.bash ros2 launch moveit2_tutorials demo.launch.py rviz_config:=panda_moveit_config_demo_empty.rviz     我们使用如下example中的例子 ws_moveit2/src/pymoveit2/examples/ex_joint_goal.py ros2 run pymoveit2 ex_joint_goal.py --ros-args -p joint_positions:="[1.57, -1.57, 0.0, -1.57, 0.0, 1.57, 0.7854]"     但不动,panda的控制台报错了,找不到arm这个group,这里需要做一点修改,因为官方的group name不是panda的group规划组名 ws_moveit2/src/pymoveit2/pymoveit2/robots/panda.py 这里面定义的组名是"arm" MOVE_GROUP_ARM: str = "arm" MOVE_GROUP_GRIPPER: str = "gripper" 所以我们可以修改ws_moveit2/src/pymoveit2/examples/ex_joint_goal.py,将 # Create MoveIt 2 interface moveit2 = MoveIt2( node=node, joint_names=robot.joint_names(), base_link_name=robot.base_link_name(), end_effector_name=robot.end_effector_name(), group_name=robot.MOVE_GROUP_ARM, callback_group=callback_group, ) 换成如下 # Create MoveIt 2 interface moveit2 = MoveIt2( node=node, joint_names=robot.joint_names(), base_link_name=robot.base_link_name(), end_effector_name=robot.end_effector_name(), group_name="panda_arm", callback_group=callback_group, ) 再colcon build一下,运行,可以看到机械臂可以运动到固定位姿了      

  • 回复了主题帖: Panda换成SO-Arm100机械臂,代码仅换一行,末端固定位姿一样控制

    Jacktang 发表于 2025-3-14 07:22 图形化编程就是直观,编程的出错率应该是可以降低的 这可不是图形化编程

  • 2025-03-13
  • 发表了主题帖: Panda换成SO-Arm100机械臂,代码仅换一行,末端固定位姿一样控制

        视频链接: 仓库代码:GitHub - LitchiCheng/ros2_package 就换一行,我们打开《一文搞定!ROS2 中用 MoveIt2 精准操控 Panda 机械臂末端至固定位姿》中写的Package,将原来的这行 moveit::planning_interface::MoveGroupInterface move_group(node, "panda-arms"); 改成如下即可 moveit::planning_interface::MoveGroupInterface move_group(node, "so-arm100-groups"); 控也能控,但Z的位置反转了,增加一个变换矩阵进行反转即可   // 定义转换矩阵 Eigen::Matrix3d transform_matrix; transform_matrix << 1, 0, 0, 0, 1, 0, 0, 0, -1; // 将位置向量转换为Eigen向量 Eigen::Vector3d original_position(target_pose.position.x, target_pose.position.y, target_pose.position.z); // 进行矩阵乘法 Eigen::Vector3d new_position = transform_matrix * original_position; // 更新目标位置 target_pose.position.x = new_position(0); target_pose.position.y = new_position(1); target_pose.position.z = new_position(2); 先运行so-arm100的moveit场景 source install/setup.bash ros2 launch so_arm100_moveit demo.lanuch.py 在运行控制位姿的程序,这里我们Package名称不变,还是之前的Panda,固定位姿 target_pose.position.x = 0.2; target_pose.position.y = 0.1; target_pose.position.z = 0.1; source install/setup.bash ros2 run panda_moveit_controller panda_moveit_control              

  • 发表了日志: Panda换成SO-Arm100机械臂,代码仅换一行,末端固定位姿一样控制

  • 回复了主题帖: OpenCV 拆分、合并图像通道方法及复现

    Jacktang 发表于 2025-3-10 07:47 次方可以验证任何颜色都可以通过红、绿、蓝这三种颜色按不同的比例混合而成的 是的,最后再合成merge就可以验证

  • 回复了主题帖: 【🚀SO-Arm100 机械臂 ROS2 配置保姆级教程】MoveIt Setup Assistant 从 0 到 1 ...

    Jacktang 发表于 2025-3-13 07:27 看来ROS2导入机械臂URDF竟有这些坑?还得好好读读 挺长的,有问题可以留言

  • 2025-03-12
  • 回复了主题帖: ROS2导入机械臂URDF竟有这些坑?SO-ARM100 Rivz可视化避坑指南

    涛耐迪 发表于 2025-3-12 17:40 每一次点击都是一次学习机会,每一次回帖都能让社会进步,每一次分享都值得点赞。 每一次评论都是鼓励版主继续发帖的动力

  • 发表了日志: 【&amp;#128640;SO-Arm100 机械臂 ROS2 配置保姆级教程】MoveIt Setup Assistant 从 ...

  • 发表了主题帖: 【&#128640;SO-Arm100 机械臂 ROS2 配置保姆级教程】MoveIt Setup Assistant 从 0 到 1 ...

        视频讲解: Package仓库地址:LitchiCheng/ros2_package   启动moveit_setup_assistant ros2 launch moveit_setup_assistant setup_assistant.launch.py     选择导入urdf文件,如果urdf中引用的package搜索不到,会直接崩溃,需要先source install/setup.bash,具体参考《ROS2导入机械臂URDF竟有这些坑?SO-ARM100 Rivz可视化避坑指南》 [moveit_setup_assistant-1] Qt: Session management error: networkIdsList argument is NULL[moveit_setup_assistant-1] terminate called after throwing an instance of 'ament_index_cpp::PackageNotFoundError' [moveit_setup_assistant-1] what(): package 'display_urdf_launch' not found, searching: [/opt/ros/humble] [moveit_setup_assistant-1] Stack trace (most recent call last): [moveit_setup_assistant-1] #31 Object "/usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5.15.3", at 0x7e3740172e46, in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool) [moveit_setup_assistant-1] #30 Object "/usr/lib/x86_64-linux-gnu/libQt5Core.so.5.15.3", at 0x7e373f4b9e39, in QCoreApplication::notifyInternal2(QObject*, QEvent*)     点击Load Files,加载完成后,右侧会出现模型,和Rviz中一样,可以拖动显示     下一步点击左侧的Self-Collsions配置关节碰撞相关的参数,如果没有出现link,勾上右下角的show enabled pairs     然后可以点击Generate Collision Matrix生成碰撞矩阵     点击左侧Virtual Joint,配置虚关节,用来将基座放置在哪儿     配置Planning Groups,这个作用在Moveit规划的文章里面我们用过,就是要指定规划的组名,比如so-arm100-groups,就是这里了,运动学求解器选择一个即可,OMPL的planning我们选常用的RTTConnect     add joints,需要把这个组里面规划的关节都选中,最后配好的效果如下     创建机器人位姿,如home位置     没有末端夹爪或其他执行器,End Effectors不用管     增加ros2_control到urdf文件中     自动添加关节轨迹控制器到Planning Group中     Moveit Controllers也是同样,电机auto     没有感知器件,如3d传感器,点云之类的也不用配置     检查下等下要生成的文件,默认都是勾上的     添加作者信息     选择工作空间下src中新建一个package,比如so_arm100_moveit,然后生成的包就会到里面     编译运行 colcon build --packages-select so_arm100_moveit source install/setup.bash ros2 launch so_arm100_moveit demo.launch.py      

  • 回复了主题帖: ROS2导入机械臂URDF竟有这些坑?SO-ARM100 Rivz可视化避坑指南

    Jacktang 发表于 2025-3-12 07:50 SO-ARM100 Rivz可视化避坑指南,很好,收藏 收藏夹里面可不能吃灰

  • 2025-03-11
  • 发表了日志: ROS2导入机械臂URDF竟有这些坑?SO-ARM100 Rivz可视化避坑指南

  • 发表了主题帖: ROS2导入机械臂URDF竟有这些坑?SO-ARM100 Rivz可视化避坑指南

        视频讲解   导入过程有两个坑,如下过程中进行解决 先下载urdf文件 https://github.com/TheRobotStudio/SO-ARM100 git clone git@github.com:TheRobotStudio/SO-ARM100.git 如下,包括urdf及meshes文件     结合《想知道两轮差速方形底盘 URDF 咋做,ROS2 配 Rviz 咋显示吗?看这里!》中介绍的如何使用在Rviz中显示urdf的Package,我们新建一个Package,命名为display_so_arm100_rviz,需要将如上meshes和urdf文件夹复制到display_so_arm100_rviz下     这里有一个坑,需要注意,就是urdf文件中的package名字需要修改下,参考如下 修改 urdf/SO_7DOF_ARM100_08h_HL_01d_URDF_01.SLDASM.urdf 文件中所有关于package的名称改成 display_so_arm100_rviz     修改CMakeLists.txt文件,增加如下 install(DIRECTORY launch meshes urdf DESTINATION share/${PROJECT_NAME}) launch脚本修改如下 import os from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch_ros.actions import Node from launch import LaunchDescription from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare def generate_launch_description(): package_name = 'display_so_arm100_rviz' urdf_name = "SO_7DOF_ARM100_08h_HL_01d_URDF_01.SLDASM.urdf" pkg_share = FindPackageShare(package=package_name).find(package_name) urdf_model_path = os.path.join(pkg_share, f'urdf/{urdf_name}') urdf_file_path = urdf_model_path with open(urdf_file_path, 'r') as file: robot_description = file.read() return LaunchDescription([ Node( package='joint_state_publisher_gui', executable='joint_state_publisher_gui', name='joint_state_publisher_gui', parameters=[{'robot_description': robot_description}] ), Node( package='robot_state_publisher', executable='robot_state_publisher', name='robot_state_publisher', parameters=[{'robot_description': robot_description}] ), Node( package='rviz2', executable='rviz2', name='rviz2' ) ]) 编译,运行 colcon build --packages-select display_so_arm100_rviz source install/setup.bash ros2 launch display_so_arm100_rviz display_urdf.py     坑2,将坐标系选成Base后,发现坐标系是倒置的,先不管发布的消息如何,将他倒过来,只需要发布一个静态TF关系,如下 ros2 run tf2_ros static_transform_publisher 0 0 0 0 0 3.14159 Base rotated_frame 再将参考坐标系宣称rotated_frame,可以看到正过来了      

最近访客

< 1/6 >

统计信息

已有436人来访过

  • 芯积分:2581
  • 好友:2
  • 主题:93
  • 回复:337

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言