1078 lines
402 KiB
Plaintext
1078 lines
402 KiB
Plaintext
|
{
|
||
|
"cells": [
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"RL policy based on the [SoloParkour: Constrained Reinforcement Learning for Visual Locomotion from Privileged Experience](https://arxiv.org/abs/2409.13678). "
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"# Flat Ground"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"## Test In Simulation"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from Go2Py.robot.fsm import FSM\n",
|
||
|
"from Go2Py.robot.remote import KeyboardRemote, XBoxRemote\n",
|
||
|
"from Go2Py.robot.safety import SafetyHypervisor\n",
|
||
|
"from Go2Py.sim.mujoco import Go2Sim\n",
|
||
|
"from Go2Py.control.neuro_diff_sim import *\n",
|
||
|
"import torch"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 2,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from Go2Py.robot.model import FrictionModel\n",
|
||
|
"friction_model = None\n",
|
||
|
"Fs = np.zeros(12)\n",
|
||
|
"mu_v = np.zeros(12)\n",
|
||
|
"#mu_v[[2,5,8,11]] = np.array([0.2167, -0.0647, -0.0420, -0.0834])\n",
|
||
|
"#Fs[[2,5,8,11]] = np.array([1.5259, 1.2380, 0.8917, 2.2461])\n",
|
||
|
"\n",
|
||
|
"#mu_v[[0,3,6,9]] = np.array([0., 0., 0., 0.])\n",
|
||
|
"#Fs[[0,3,6,9]] = np.array([1.5, 1.5, 1.5, 1.5])\n",
|
||
|
"#mu_v[[2,5,8,11]] = np.array([0., 0., 0., 0.])\n",
|
||
|
"#Fs[[2,5,8,11]] = np.array([1.5, 1.5, 1.5, 1.5])\n",
|
||
|
"\n",
|
||
|
"friction_model = FrictionModel(Fs=1.5, mu_v=0.3)\n",
|
||
|
"#friction_model = FrictionModel(Fs=0., mu_v=0.)\n",
|
||
|
"#friction_model = FrictionModel(Fs=Fs, mu_v=mu_v)\n",
|
||
|
"robot = Go2Sim(dt = 0.001, friction_model=friction_model)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 3,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"remote = KeyboardRemote() # XBoxRemote() # KeyboardRemote()\n",
|
||
|
"robot.sitDownReset()\n",
|
||
|
"safety_hypervisor = SafetyHypervisor(robot)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 4,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"def getRemote(remote):\n",
|
||
|
" commands = remote.getCommands()\n",
|
||
|
" commands[0] *= 0.6\n",
|
||
|
" commands[1] *= 0.6\n",
|
||
|
" zero_commands_xy = np.linalg.norm(commands[:2]) <= 0.2\n",
|
||
|
" zero_commands_yaw = np.abs(commands[2]) <= 0.2\n",
|
||
|
" if zero_commands_xy:\n",
|
||
|
" commands[:2] = np.zeros_like(commands[:2])\n",
|
||
|
" if zero_commands_yaw:\n",
|
||
|
" commands[2] = 0\n",
|
||
|
" return commands"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 5,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"class NeuroDiffSimController:\n",
|
||
|
" def __init__(self, robot, remote, checkpoint):\n",
|
||
|
" self.remote = remote\n",
|
||
|
" self.robot = robot\n",
|
||
|
" self.policy = Policy(checkpoint)\n",
|
||
|
" self.command_profile = CommandInterface()\n",
|
||
|
" self.agent = NeuroDiffSimAgent(self.command_profile, self.robot)\n",
|
||
|
" self.hist_data = {}\n",
|
||
|
"\n",
|
||
|
" def init(self):\n",
|
||
|
" self.obs = self.agent.reset()\n",
|
||
|
" self.policy_info = {}\n",
|
||
|
" self.command_profile.yaw_vel_cmd = 0.0\n",
|
||
|
" self.command_profile.x_vel_cmd = 0.0\n",
|
||
|
" self.command_profile.y_vel_cmd = 0.0\n",
|
||
|
"\n",
|
||
|
" def update(self, robot, remote):\n",
|
||
|
" if not hasattr(self, \"obs\"):\n",
|
||
|
" self.init()\n",
|
||
|
" commands = getRemote(remote)\n",
|
||
|
" self.command_profile.yaw_vel_cmd = -commands[2]\n",
|
||
|
" self.command_profile.x_vel_cmd = commands[1]\n",
|
||
|
" self.command_profile.y_vel_cmd = -commands[0]\n",
|
||
|
"\n",
|
||
|
" self.obs = self.agent.get_obs()\n",
|
||
|
" action = self.policy(self.obs, self.policy_info)\n",
|
||
|
" _, self.ret, self.done, self.info = self.agent.step(action)\n",
|
||
|
" for key, value in self.info.items():\n",
|
||
|
" if key in self.hist_data:\n",
|
||
|
" self.hist_data[key].append(value)\n",
|
||
|
" else:\n",
|
||
|
" self.hist_data[key] = [value]"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"robot.getJointStates()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from Go2Py import ASSETS_PATH\n",
|
||
|
"import os\n",
|
||
|
"checkpoint_path = os.path.join(ASSETS_PATH, 'checkpoints/neuro_diff_sim/neuro_diff_sim.pt')\n",
|
||
|
"\n",
|
||
|
"controller = NeuroDiffSimController(robot, remote, checkpoint_path)\n",
|
||
|
"decimation = 20\n",
|
||
|
"fsm = FSM(robot, remote, safety_hypervisor, control_dT=decimation * robot.dt, user_controller_callback=controller.update)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"# Slippage Analysis"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 8,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"contacts = []\n",
|
||
|
"feet_vels = []\n",
|
||
|
"\n",
|
||
|
"while True:\n",
|
||
|
" if remote.xbox_controller.digital_cmd[1]:\n",
|
||
|
" break\n",
|
||
|
" contact_state = robot.getFootContact()>15\n",
|
||
|
" sites = ['FR_foot', 'FL_foot', 'RR_foot', 'RL_foot']\n",
|
||
|
" feet_vel = [np.linalg.norm(robot.getFootVelInWorld(s)) for s in sites]\n",
|
||
|
" contacts.append(contact_state)\n",
|
||
|
" feet_vels.append(feet_vel)\n",
|
||
|
" time.sleep(0.01)\n",
|
||
|
"\n",
|
||
|
"feet_vels = np.stack(feet_vels)\n",
|
||
|
"contacts = np.stack(contacts)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import matplotlib.pyplot as plt\n",
|
||
|
"start = 300\n",
|
||
|
"end = 1200\n",
|
||
|
"plt.plot(contacts[start:end,0])\n",
|
||
|
"plt.plot(feet_vels[start:end,0])\n",
|
||
|
"plt.legend(['contact state', 'foot velocity'])\n",
|
||
|
"plt.grid(True)\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.savefig('foot_slipping_fric0.2.png')"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"**To Do**\n",
|
||
|
"- Train a policy without any actuator friction and check the plots for friction 0.2 and 0.6 \n",
|
||
|
"- Do the same experiment for the walk-these-ways policy\n",
|
||
|
"- While testing the walk these ways, check the output of the adaptation module for various friction numbers, any correlation?"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 8,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"fsm.close()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"## Foot Contanct Analysis"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"np.array(controller.hist_data[\"body_pos\"])[0, 0, -1]"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"plt.plot(np.array(controller.hist_data[\"body_pos\"])[:, 0, -1])"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import matplotlib.pyplot as plt\n",
|
||
|
"# Assuming 'controller.hist_data[\"torques\"]' is a dictionary with torque profiles\n",
|
||
|
"torques = np.array(controller.hist_data[\"body_linear_vel\"])[:, 0, :, 0]\n",
|
||
|
"\n",
|
||
|
"# Number of torque profiles\n",
|
||
|
"torque_nb = torques.shape[1]\n",
|
||
|
"\n",
|
||
|
"# Number of rows needed for the grid, with 3 columns per row\n",
|
||
|
"n_cols = 3\n",
|
||
|
"n_rows = int(np.ceil(torque_nb / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing (in case of multiple rows)\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot each torque profile\n",
|
||
|
"for i in range(torque_nb):\n",
|
||
|
" axes[i].plot(np.arange(torques.shape[0]) * robot.dt * decimation, torques[:, i])\n",
|
||
|
" axes[i].set_title(f'Torque {i+1}')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Torque Value')\n",
|
||
|
" axes[i].grid(True)\n",
|
||
|
"\n",
|
||
|
"# Remove any empty subplots if torque_nb is not a multiple of 3\n",
|
||
|
"for j in range(torque_nb, len(axes)):\n",
|
||
|
" fig.delaxes(axes[j])\n",
|
||
|
"\n",
|
||
|
"# Adjust layout\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.savefig(\"torque_profile.png\")\n",
|
||
|
"plt.show()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import matplotlib.pyplot as plt\n",
|
||
|
"# Assuming 'controller.hist_data[\"torques\"]' is a dictionary with torque profiles\n",
|
||
|
"torques = np.array(controller.hist_data[\"torques\"])\n",
|
||
|
"\n",
|
||
|
"# Number of torque profiles\n",
|
||
|
"torque_nb = torques.shape[1]\n",
|
||
|
"\n",
|
||
|
"# Number of rows needed for the grid, with 3 columns per row\n",
|
||
|
"n_cols = 3\n",
|
||
|
"n_rows = int(np.ceil(torque_nb / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing (in case of multiple rows)\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot each torque profile\n",
|
||
|
"for i in range(torque_nb):\n",
|
||
|
" axes[i].plot(np.arange(torques.shape[0]) * robot.dt * decimation, torques[:, i])\n",
|
||
|
" axes[i].set_title(f'Torque {i+1}')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Torque Value')\n",
|
||
|
" axes[i].grid(True)\n",
|
||
|
"\n",
|
||
|
"# Remove any empty subplots if torque_nb is not a multiple of 3\n",
|
||
|
"for j in range(torque_nb, len(axes)):\n",
|
||
|
" fig.delaxes(axes[j])\n",
|
||
|
"\n",
|
||
|
"# Adjust layout\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.savefig(\"torque_profile.png\")\n",
|
||
|
"plt.show()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Extract the joint position data for the first joint over time\n",
|
||
|
"joint_pos = np.array(controller.hist_data[\"joint_vel\"])[:, 0]\n",
|
||
|
"\n",
|
||
|
"# Number of data points in joint_pos\n",
|
||
|
"n_data_points = len(joint_pos)\n",
|
||
|
"\n",
|
||
|
"# Since you're plotting only one joint, no need for multiple subplots in this case.\n",
|
||
|
"# But to follow the grid requirement, we'll replicate the data across multiple subplots.\n",
|
||
|
"# For example, let's assume you want to visualize this data 9 times in a 3x3 grid.\n",
|
||
|
"\n",
|
||
|
"n_cols = 3\n",
|
||
|
"n_rows = int(np.ceil(torque_nb / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing (in case of multiple rows)\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot the same joint position data in every subplot (as per grid requirement)\n",
|
||
|
"for i in range(n_rows * n_cols):\n",
|
||
|
" axes[i].plot(joint_pos[:, i])\n",
|
||
|
" axes[i].set_title(f'Joint Position {i+1}')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Position Value')\n",
|
||
|
"\n",
|
||
|
"# Adjust layout\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.savefig(\"joint_position_profile.png\")\n",
|
||
|
"plt.show()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import matplotlib.pyplot as plt\n",
|
||
|
"# Assuming 'controller.hist_data[\"foot_contact_forces_mag\"]' is a dictionary with foot contact force magnitudes\n",
|
||
|
"foot_contact_forces_mag = np.array(controller.hist_data[\"foot_contact_forces_mag\"])\n",
|
||
|
"\n",
|
||
|
"# Number of feet (foot_nb)\n",
|
||
|
"foot_nb = foot_contact_forces_mag.shape[1]\n",
|
||
|
"\n",
|
||
|
"# Number of rows needed for the grid, with 3 columns per row\n",
|
||
|
"n_cols = 3\n",
|
||
|
"n_rows = int(np.ceil(foot_nb / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing (in case of multiple rows)\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot each foot's contact force magnitude\n",
|
||
|
"for i in range(foot_nb):\n",
|
||
|
" axes[i].plot(foot_contact_forces_mag[:, i])\n",
|
||
|
" axes[i].set_title(f'Foot {i+1} Contact Force Magnitude')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Force Magnitude')\n",
|
||
|
"\n",
|
||
|
"# Remove any empty subplots if foot_nb is not a multiple of 3\n",
|
||
|
"for j in range(foot_nb, len(axes)):\n",
|
||
|
" fig.delaxes(axes[j])\n",
|
||
|
"\n",
|
||
|
"# Adjust layout\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.savefig(\"foot_contact_profile.png\")\n",
|
||
|
"plt.show()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Extract the joint acceleration data for the first joint over time\n",
|
||
|
"joint_acc = np.array(controller.hist_data[\"joint_acc\"])[:, 0]\n",
|
||
|
"\n",
|
||
|
"# Number of data points in joint_acc\n",
|
||
|
"n_data_points = len(joint_acc)\n",
|
||
|
"\n",
|
||
|
"# Number of feet (foot_nb)\n",
|
||
|
"foot_nb = joint_acc.shape[1]\n",
|
||
|
"\n",
|
||
|
"# Number of rows needed for the grid, with 3 columns per row\n",
|
||
|
"n_cols = 3\n",
|
||
|
"n_rows = int(np.ceil(foot_nb / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot the same joint acceleration data in every subplot (as per grid requirement)\n",
|
||
|
"for i in range(n_rows * n_cols):\n",
|
||
|
" axes[i].plot(joint_acc[:, i])\n",
|
||
|
" axes[i].set_title(f'Joint Acceleration {i+1}')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Acceleration Value')\n",
|
||
|
"\n",
|
||
|
"# Adjust layout to prevent overlap\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.show()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Extract the joint jerk data over time\n",
|
||
|
"joint_jerk = np.array(controller.hist_data[\"joint_jerk\"])[:, 0]\n",
|
||
|
"\n",
|
||
|
"# Number of data points in joint_jerk\n",
|
||
|
"n_data_points = len(joint_jerk)\n",
|
||
|
"\n",
|
||
|
"# Number of joints (assuming the second dimension corresponds to joints)\n",
|
||
|
"num_joints = joint_jerk.shape[1]\n",
|
||
|
"\n",
|
||
|
"# Number of columns per row in the subplot grid\n",
|
||
|
"n_cols = 3\n",
|
||
|
"# Number of rows needed for the grid\n",
|
||
|
"n_rows = int(np.ceil(num_joints / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot the joint jerk data for each joint\n",
|
||
|
"for i in range(num_joints):\n",
|
||
|
" axes[i].plot(joint_jerk[:, i])\n",
|
||
|
" axes[i].set_title(f'Joint Jerk {i+1}')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Jerk Value')\n",
|
||
|
"\n",
|
||
|
"# Hide any unused subplots\n",
|
||
|
"for i in range(num_joints, len(axes)):\n",
|
||
|
" fig.delaxes(axes[i])\n",
|
||
|
"\n",
|
||
|
"# Adjust layout to prevent overlap\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.show()\n"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Extract the foot contact rate data over time\n",
|
||
|
"foot_contact_rate = np.array(controller.hist_data[\"foot_contact_rate\"])[:, 0]\n",
|
||
|
"\n",
|
||
|
"# Number of data points in foot_contact_rate\n",
|
||
|
"n_data_points = foot_contact_rate.shape[0]\n",
|
||
|
"\n",
|
||
|
"# Number of feet (assuming the second dimension corresponds to feet)\n",
|
||
|
"num_feet = foot_contact_rate.shape[1]\n",
|
||
|
"\n",
|
||
|
"# Number of columns per row in the subplot grid\n",
|
||
|
"n_cols = 3\n",
|
||
|
"# Number of rows needed for the grid\n",
|
||
|
"n_rows = int(np.ceil(num_feet / n_cols))\n",
|
||
|
"\n",
|
||
|
"# Create the figure and axes for subplots\n",
|
||
|
"fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))\n",
|
||
|
"\n",
|
||
|
"# Flatten the axes array for easy indexing\n",
|
||
|
"axes = axes.flatten()\n",
|
||
|
"\n",
|
||
|
"# Plot the foot contact rate data for each foot\n",
|
||
|
"for i in range(num_feet):\n",
|
||
|
" axes[i].plot(foot_contact_rate[:, i])\n",
|
||
|
" axes[i].set_title(f'Foot Contact Rate {i+1}')\n",
|
||
|
" axes[i].set_xlabel('Time')\n",
|
||
|
" axes[i].set_ylabel('Contact Rate')\n",
|
||
|
"\n",
|
||
|
"# Hide any unused subplots\n",
|
||
|
"for i in range(num_feet, len(axes)):\n",
|
||
|
" fig.delaxes(axes[i])\n",
|
||
|
"\n",
|
||
|
"# Adjust layout to prevent overlap\n",
|
||
|
"plt.tight_layout()\n",
|
||
|
"plt.show()\n"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"## Test on Real Robot (ToDo)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from Go2Py.robot.fsm import FSM\n",
|
||
|
"from Go2Py.robot.remote import XBoxRemote\n",
|
||
|
"from Go2Py.robot.safety import SafetyHypervisor\n",
|
||
|
"from Go2Py.control.neuro_diff_sim import *"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 2,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from Go2Py.robot.interface import GO2Real\n",
|
||
|
"import numpy as np\n",
|
||
|
"robot = GO2Real(mode='lowlevel')"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"remote = XBoxRemote() # KeyboardRemote()\n",
|
||
|
"safety_hypervisor = SafetyHypervisor(robot)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"robot.getJointStates()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Make sure the robot can take commands from python. The next cell should make the joints free to move (no damping)."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import numpy as np\n",
|
||
|
"import time\n",
|
||
|
"start_time = time.time()\n",
|
||
|
"\n",
|
||
|
"while time.time()-start_time < 30:\n",
|
||
|
" q = np.zeros(12) \n",
|
||
|
" dq = np.zeros(12)\n",
|
||
|
" kp = np.ones(12)*0.0\n",
|
||
|
" kd = np.ones(12)*0.0\n",
|
||
|
" tau = np.zeros(12)\n",
|
||
|
" tau[0] = 0.0\n",
|
||
|
" robot.setCommands(q, dq, kp, kd, tau)\n",
|
||
|
" time.sleep(0.02)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 6,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"def getRemote(remote):\n",
|
||
|
" commands = remote.getCommands()\n",
|
||
|
" commands[0] *= 0.6\n",
|
||
|
" commands[1] *= 0.6\n",
|
||
|
" zero_commands = np.logical_and(\n",
|
||
|
" np.linalg.norm(commands[:2]) <= 0.2,\n",
|
||
|
" np.abs(commands[2]) <= 0.2\n",
|
||
|
" )\n",
|
||
|
" if zero_commands:\n",
|
||
|
" commands = np.zeros_like(commands)\n",
|
||
|
" return commands"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 7,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"class NeuroDiffSimController:\n",
|
||
|
" def __init__(self, robot, remote, checkpoint):\n",
|
||
|
" self.remote = remote\n",
|
||
|
" self.robot = robot\n",
|
||
|
" self.policy = Policy(checkpoint)\n",
|
||
|
" self.command_profile = CommandInterface()\n",
|
||
|
" self.agent = NeuroDiffSimAgent(self.command_profile, self.robot)\n",
|
||
|
" self.hist_data = {}\n",
|
||
|
"\n",
|
||
|
" def init(self):\n",
|
||
|
" self.obs = self.agent.reset()\n",
|
||
|
" self.policy_info = {}\n",
|
||
|
" self.command_profile.yaw_vel_cmd = 0.0\n",
|
||
|
" self.command_profile.x_vel_cmd = 0.0\n",
|
||
|
" self.command_profile.y_vel_cmd = 0.0\n",
|
||
|
"\n",
|
||
|
" def update(self, robot, remote):\n",
|
||
|
" if not hasattr(self, \"obs\"):\n",
|
||
|
" self.init()\n",
|
||
|
" commands = getRemote(remote)\n",
|
||
|
" self.command_profile.yaw_vel_cmd = -commands[2]\n",
|
||
|
" self.command_profile.x_vel_cmd = commands[1]\n",
|
||
|
" self.command_profile.y_vel_cmd = -commands[0]\n",
|
||
|
"\n",
|
||
|
" self.obs = self.agent.get_obs()\n",
|
||
|
" action = self.policy(self.obs, self.policy_info)\n",
|
||
|
" _, self.ret, self.done, self.info = self.agent.step(action)\n",
|
||
|
" for key, value in self.info.items():\n",
|
||
|
" if key in self.hist_data:\n",
|
||
|
" self.hist_data[key].append(value)\n",
|
||
|
" else:\n",
|
||
|
" self.hist_data[key] = [value]"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from Go2Py import ASSETS_PATH \n",
|
||
|
"import os\n",
|
||
|
"checkpoint_path = os.path.join(ASSETS_PATH, 'checkpoints/neuro_diff_sim/neuro_diff_sim.pt')\n",
|
||
|
"\n",
|
||
|
"controller = NeuroDiffSimController(robot, remote, checkpoint_path)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 10,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"fsm = FSM(robot, remote, safety_hypervisor, control_dT=1./50., user_controller_callback=controller.update)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 10,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"fsm.close()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": []
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": []
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": []
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 1,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"ename": "ModuleNotFoundError",
|
||
|
"evalue": "No module named 'rclpy'",
|
||
|
"output_type": "error",
|
||
|
"traceback": [
|
||
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||
|
"\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
|
||
|
"Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mGo2Py\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mros2\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m PoindCloud2Bridge, ros2_init, ros2_close, ROS2ExecutorManager, ROS2CameraReader\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mGo2Py\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mros2\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m ROS2OdometryReader\n\u001b[1;32m 3\u001b[0m ros2_init()\n",
|
||
|
"File \u001b[0;32m/home/Go2py/Go2Py/utils/ros2.py:5\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mtime\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mnumpy\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[0;32m----> 5\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mrclpy\u001b[39;00m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mtf2_ros\u001b[39;00m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mrclpy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnode\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Node\n",
|
||
|
"\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'rclpy'"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"from Go2Py.utils.ros2 import PoindCloud2Bridge, ros2_init, ros2_close, ROS2ExecutorManager, ROS2CameraReader\n",
|
||
|
"from Go2Py.utils.ros2 import ROS2OdometryReader\n",
|
||
|
"ros2_init()\n",
|
||
|
"map_bridge = PoindCloud2Bridge('/Laser_map')\n",
|
||
|
"fast_lio_odom = ROS2OdometryReader('/Odometry', 'odometry_subscriber')\n",
|
||
|
"rgb_camera_bridge = ROS2CameraReader('/camera/color/image_raw', 'rgb_reader')\n",
|
||
|
"depth_camera_bridge = ROS2CameraReader('/camera/depth/image_rect_raw', 'depth_reader')\n",
|
||
|
"\n",
|
||
|
"ros2_exec_manager = ROS2ExecutorManager()\n",
|
||
|
"ros2_exec_manager.add_node(map_bridge)\n",
|
||
|
"ros2_exec_manager.add_node(fast_lio_odom)\n",
|
||
|
"ros2_exec_manager.add_node(rgb_camera_bridge)\n",
|
||
|
"ros2_exec_manager.add_node(depth_camera_bridge)\n",
|
||
|
"ros2_exec_manager.start()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 2,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import numpy as np\n",
|
||
|
"body_T_lidar = np.array([\n",
|
||
|
" [ 0.9743701, 0.0, 0.2249511, 0.1870 ],\n",
|
||
|
" [ 0.0, 1.0, 0.0, 0.0 ],\n",
|
||
|
" [-0.2249511, 0.0, 0.9743701, 0.0803 ],\n",
|
||
|
" [ 0.0, 0.0, 0.0, 1.0 ]\n",
|
||
|
"])"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 4,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"text/plain": [
|
||
|
"array([[ 0.97319254, 0.00978982, -0.22978327, -0.29256741],\n",
|
||
|
" [-0.01415969, 0.99974875, -0.01737615, -0.05825406],\n",
|
||
|
" [ 0.22955543, 0.02016401, 0.97308665, -0.03344418],\n",
|
||
|
" [ 0. , 0. , 0. , 1. ]])"
|
||
|
]
|
||
|
},
|
||
|
"execution_count": 4,
|
||
|
"metadata": {},
|
||
|
"output_type": "execute_result"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"lio_T_lidar = fast_lio_odom.get_pose()\n",
|
||
|
"lio_T_body = lio_T_lidar @ np.linalg.inv(body_T_lidar)\n",
|
||
|
"lio_T_body"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 5,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import matplotlib.pyplot as plt\n",
|
||
|
"import cv2\n",
|
||
|
"depth = depth_camera_bridge.get_image()\n",
|
||
|
"depth = cv2.resize(depth, (320, 240))\n"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 6,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"text/plain": [
|
||
|
"15124"
|
||
|
]
|
||
|
},
|
||
|
"execution_count": 6,
|
||
|
"metadata": {},
|
||
|
"output_type": "execute_result"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"depth.max()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 7,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"text/plain": [
|
||
|
"<matplotlib.image.AxesImage at 0xffff46306070>"
|
||
|
]
|
||
|
},
|
||
|
"execution_count": 7,
|
||
|
"metadata": {},
|
||
|
"output_type": "execute_result"
|
||
|
},
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "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
|
||
|
"text/plain": [
|
||
|
"<Figure size 640x480 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"plt.imshow(rgb_camera_bridge.get_image())"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 1,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import socket\n",
|
||
|
"import struct\n",
|
||
|
"import numpy as np\n",
|
||
|
"import time\n",
|
||
|
"import threading\n",
|
||
|
"from cyclonedds.domain import DomainParticipant\n",
|
||
|
"from cyclonedds.topic import Topic\n",
|
||
|
"from cyclonedds.sub import DataReader\n",
|
||
|
"from cyclonedds.pub import Publisher, DataWriter\n",
|
||
|
"from dataclasses import dataclass\n",
|
||
|
"from Go2DDSMsgs import PointCloud, RGBImage, DepthImage, Pose, HighCommand\n",
|
||
|
"from scipy.spatial.transform import Rotation as R\n",
|
||
|
"\n",
|
||
|
"\n",
|
||
|
"def get_last_msg(reader, topic_type):\n",
|
||
|
" \"\"\" \"\"\"\n",
|
||
|
" last_msg = reader.take()\n",
|
||
|
"\n",
|
||
|
" if last_msg:\n",
|
||
|
" while True:\n",
|
||
|
" a = reader.take()\n",
|
||
|
" if not a:\n",
|
||
|
" break\n",
|
||
|
" else:\n",
|
||
|
" last_msg = a\n",
|
||
|
" if last_msg:\n",
|
||
|
" msg = last_msg[0]\n",
|
||
|
" if type(msg) == topic_type:\n",
|
||
|
" return msg\n",
|
||
|
" else:\n",
|
||
|
" return None\n",
|
||
|
"\n",
|
||
|
" else:\n",
|
||
|
" return None\n",
|
||
|
" \n",
|
||
|
"class Go2DDSServer:\n",
|
||
|
" def __init__(self, robot_name):\n",
|
||
|
" self.robot_name = robot_name\n",
|
||
|
" self.domain = DomainParticipant()\n",
|
||
|
" self.rgb_topic = Topic(self.domain, f'{self.robot_name}_rgb', RGBImage)\n",
|
||
|
" self.depth_topic = Topic(self.domain, f'{self.robot_name}_depth', DepthImage) \n",
|
||
|
" self.map_topic = Topic(self.domain, f'{self.robot_name}_lio_map', PointCloud)\n",
|
||
|
" self.lidar_odom_topic = Topic(self.domain, f'{self.robot_name}_lio_odom', Pose)\n",
|
||
|
" self.high_cmd_topic = Topic(self.domain, f'{self.robot_name}_high_cmd', HighCommand)\n",
|
||
|
" self.rgb_publisher = Publisher(self.domain)\n",
|
||
|
" self.depth_publisher = Publisher(self.domain)\n",
|
||
|
" self.map_publisher = Publisher(self.domain)\n",
|
||
|
" self.lidar_odom_publisher = Publisher(self.domain)\n",
|
||
|
" self.high_cmd_reader = DataReader(self.domain, self.high_cmd_topic)\n",
|
||
|
" self.rgb_writer = DataWriter(self.rgb_publisher, self.rgb_topic)\n",
|
||
|
" self.depth_writer = DataWriter(self.depth_publisher, self.depth_topic)\n",
|
||
|
" self.map_writer = DataWriter(self.map_publisher, self.map_topic)\n",
|
||
|
" self.lidar_odom_writer = DataWriter(self.lidar_odom_publisher, self.lidar_odom_topic)\n",
|
||
|
" \n",
|
||
|
" \n",
|
||
|
" \n",
|
||
|
"\n",
|
||
|
" def sendRGB(self, rgb):\n",
|
||
|
" self.rgb_writer.write(RGBImage(data=rgb.reshape(-1).tolist(), width=rgb.shape[1], height=rgb.shape[0], timestamp=''))\n",
|
||
|
" \n",
|
||
|
" def sendDepth(self, depth):\n",
|
||
|
" self.depth_writer.write(DepthImage(data=depth.reshape(-1).tolist(), width=depth.shape[1], height=depth.shape[0], timestamp=''))\n",
|
||
|
"\n",
|
||
|
" def sendMap(self, pcd):\n",
|
||
|
" map_pcd_mgs = PointCloud(x = pcd[:,0].tolist(), \n",
|
||
|
" y = pcd[:,1].tolist(), \n",
|
||
|
" z = pcd[:,2].tolist(), \n",
|
||
|
" timestamp=''\n",
|
||
|
" )\n",
|
||
|
" self.map_writer.write(map_pcd_mgs)\n",
|
||
|
" \n",
|
||
|
" def sendLidarOdom(self, lio_T_body):\n",
|
||
|
" q = R.from_matrix(lio_T_body[:3,:3]).as_quat()\n",
|
||
|
" t = lio_T_body[:3,3]\n",
|
||
|
" pose_msg = Pose(quat=q.tolist(), trans=t.tolist(), timestamp='')\n",
|
||
|
" self.lidar_odom_writer.write(pose_msg)\n",
|
||
|
"\n",
|
||
|
" def getHighCmd(self):\n",
|
||
|
" return get_last_msg(self.high_cmd_reader, HighCommand) \n",
|
||
|
" \n",
|
||
|
"\n",
|
||
|
"# class Go2DDSClient:\n",
|
||
|
"# def __init__(self, robot_name):\n",
|
||
|
"# self.robot_name = robot_name\n",
|
||
|
"# self.domain = DomainParticipant()\n",
|
||
|
"# self.rgb_topic = Topic(self.domain, f'{self.robot_name}_rgb', RGBImage)\n",
|
||
|
"# self.depth_topic = Topic(self.domain, f'{self.robot_name}_depth', DepthImage) \n",
|
||
|
"# self.map_topic = Topic(self.domain, f'{self.robot_name}_lio_map', PointCloud)\n",
|
||
|
"# self.lidar_odom_topic = Topic(self.domain, f'{self.robot_name}_lio_odom', Pose)\n",
|
||
|
"# self.high_cmd_topic = Topic(self.domain, f'{self.robot_name}_high_cmd', HighCommand)\n",
|
||
|
"# self.rgb_reader = DataReader(self.domain, self.rgb_topic)\n",
|
||
|
"# self.depth_reader = DataReader(self.domain, self.depth_topic)\n",
|
||
|
"# self.map_reader = DataReader(self.domain, self.map_topic)\n",
|
||
|
"# self.lidar_odom_reader = DataReader(self.domain, self.lidar_odom_topic)\n",
|
||
|
"# self.high_cmd_writer = DataWriter(self.domain, self.high_cmd_topic)\n",
|
||
|
"\n",
|
||
|
"# def getRGB(self):\n",
|
||
|
"# return get_last_msg(self.rgb_reader, RGBImage)\n",
|
||
|
" \n",
|
||
|
"# def getDepth(self):\n",
|
||
|
"# return get_last_msg(self.depth_reader, DepthImage)\n",
|
||
|
" \n",
|
||
|
"# def getMap(self):\n",
|
||
|
"# return get_last_msg(self.map_reader, PointCloud)\n",
|
||
|
" \n",
|
||
|
"# def getLidarOdom(self):\n",
|
||
|
"# return get_last_msg(self.lidar_odom_reader, Pose)\n",
|
||
|
" \n",
|
||
|
"# def sendHighCmd(self, vx, vy, omega):\n",
|
||
|
"# self.high_cmd_writer.write(HighCommand(vx=vx, vy=vy, omega=omega, timestamp=''))\n",
|
||
|
" "
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 1,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from dds_telemetry import Go2DDSServer, Go2DDSClient"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": []
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 2,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"server = Go2DDSServer(robot_name='go2')"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 2,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"client = Go2DDSClient(robot_name='go2')"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 6,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"img = client.getRGB()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 19,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"client.sendHighCmd(0,0,0)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 23,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"text/plain": [
|
||
|
"HighCommand(vx=0.0, vy=0.0, omega=0.0, timestamp='')"
|
||
|
]
|
||
|
},
|
||
|
"execution_count": 23,
|
||
|
"metadata": {},
|
||
|
"output_type": "execute_result"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"server.getHighCmd()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 3,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import numpy as np\n",
|
||
|
"rgb = np.random.randint(0, 255, (10, 10, 3)).astype(np.uint8)\n",
|
||
|
"server.sendRGB(rgb)\n"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"metadata": {
|
||
|
"kernelspec": {
|
||
|
"display_name": "usr",
|
||
|
"language": "python",
|
||
|
"name": "python3"
|
||
|
},
|
||
|
"language_info": {
|
||
|
"codemirror_mode": {
|
||
|
"name": "ipython",
|
||
|
"version": 3
|
||
|
},
|
||
|
"file_extension": ".py",
|
||
|
"mimetype": "text/x-python",
|
||
|
"name": "python",
|
||
|
"nbconvert_exporter": "python",
|
||
|
"pygments_lexer": "ipython3",
|
||
|
"version": "3.8.10"
|
||
|
}
|
||
|
},
|
||
|
"nbformat": 4,
|
||
|
"nbformat_minor": 2
|
||
|
}
|