Adds Simulator from OpenQuadruped/spot_mini_mini

This commit is contained in:
Rune Harlyk
2024-03-04 15:55:45 +01:00
parent 02871591fd
commit 6a981b64fa
162 changed files with 11940 additions and 1 deletions
@@ -0,0 +1,118 @@
#include "mini_ros/spot.hpp"
namespace spot
{
// Spot Constructor
Spot::Spot()
{
cmd.x_velocity = 0.0;
cmd.y_velocity = 0.0;
cmd.rate = 0.0;
cmd.roll = 0.0;
cmd.pitch = 0.0;
cmd.yaw = 0.0;
cmd.z = 0.0;
cmd.motion = Stop;
cmd.movement = Viewing;
}
void Spot::update_command(const double & vx, const double & vy, const double & z,
const double & w, const double & wx, const double & wy)
{
// If Command is nearly zero, just give zero
if (almost_equal(vx, 0.0) and almost_equal(vy, 0.0) and almost_equal(z, 0.0) and almost_equal(w, 0.0))
{
cmd.motion = Stop;
cmd.x_velocity = 0.0;
cmd.y_velocity = 0.0;
cmd.rate = 0.0;
cmd.roll = 0.0;
cmd.pitch = 0.0;
cmd.yaw = 0.0;
cmd.z = 0.0;
cmd.faster = 0.0;
cmd.slower = 0.0;
} else
{
cmd.motion = Go;
if (cmd.movement == Stepping)
{
// Stepping Mode, use commands as vx, vy, rate, Z
cmd.x_velocity = vx;
cmd.y_velocity = vy;
cmd.rate = w;
cmd.z = z;
cmd.roll = 0.0;
cmd.pitch = 0.0;
cmd.yaw = 0.0;
// change clearance height from +- 0-2 * scaling
cmd.faster = 1.0 - wx;
cmd.slower = -(1.0 - wy);
} else
{
// Viewing Mode, use commands as RPY, Z
cmd.x_velocity = 0.0;
cmd.y_velocity = 0.0;
cmd.rate = 0.0;
cmd.roll = vy;
cmd.pitch = vx;
cmd.yaw = w;
cmd.z = z;
cmd.faster = 0.0;
cmd.slower = 0.0;
}
}
}
void Spot::switch_movement()
{
if (!almost_equal(cmd.x_velocity, 0.0) and !almost_equal(cmd.y_velocity, 0.0) and !almost_equal(cmd.rate, 0.0))
{
ROS_WARN("MAKE SURE BOTH LINEAR [%.2f, %.2f] AND ANGULAR VELOCITY [%.2f] ARE AT 0.0 BEFORE SWITCHING!", cmd.x_velocity, cmd.y_velocity, cmd.rate);
ROS_WARN("STOPPING ROBOT...");
cmd.motion = Stop;
cmd.x_velocity = 0.0;
cmd.y_velocity = 0.0;
cmd.rate = 0.0;
cmd.roll = 0.0;
cmd.pitch = 0.0;
cmd.yaw = 0.0;
cmd.z = 0.0;
cmd.faster = 0.0;
cmd.slower = 0.0;
} else
{
cmd.x_velocity = 0.0;
cmd.y_velocity = 0.0;
cmd.rate = 0.0;
cmd.roll = 0.0;
cmd.pitch = 0.0;
cmd.yaw = 0.0;
cmd.z = 0.0;
cmd.faster = 0.0;
cmd.slower = 0.0;
if (cmd.movement == Viewing)
{
ROS_INFO("SWITCHING TO STEPPING MOTION, COMMANDS NOW MAPPED TO VX|VY|W|Z.");
cmd.movement = Stepping;
cmd.motion = Stop;
} else
{
ROS_INFO("SWITCHING TO VIEWING MOTION, COMMANDS NOW MAPPED TO R|P|Y|Z.");
cmd.movement = Viewing;
cmd.motion = Stop;
}
}
}
SpotCommand Spot::return_command()
{
return cmd;
}
}
@@ -0,0 +1,102 @@
#include "mini_ros/teleop.hpp"
namespace tele
{
Teleop::Teleop(const int & linear_x, const int & linear_y, const int & linear_z,
const int & angular, const double & l_scale, const double & a_scale,
const int & LB, const int & RB, const int & B_scale, const int & LT,
const int & RT, const int & UD, const int & LR,
const int & sw, const int & es)
{
// step length or pitch
linear_x_ = linear_x;
// lateral fraction or roll
linear_y_ = linear_y;
// height
linear_z_ = linear_z;
// yaw rate or yaw
angular_ = angular;
// scale linear axis (usually just 1)
l_scale_ = l_scale;
// scale angular axis (usually just 1)
a_scale_ = a_scale;
// for incrementing and decrementing step velocity
// Bottom Bumpers
RB_ = RB;
LB_ = LB;
// scale Bottom Trigger axis (usually just 1)
B_scale_ = B_scale;
// Top Bumpers
RT_ = RT;
LT_ = LT;
// Switch between Stepping and Viewing
sw_ = sw;
// E-STOP
es_ = es;
// Arrow PAd
UD_ = UD;
LR_ = LR;
switch_trigger = false;
ESTOP = false;
updown = 0;
leftright = 0;
left_bump = false;
right_bump = false;
}
void Teleop::joyCallback(const sensor_msgs::Joy::ConstPtr& joy)
{
twist.linear.x = l_scale_*joy->axes[linear_x_];
twist.linear.y = l_scale_*joy->axes[linear_y_];
// NOTE: used to control robot height
twist.linear.z = -l_scale_*joy->axes[linear_z_];
twist.angular.z = a_scale_*joy->axes[angular_];
// NOTE: bottom bumpers used for changing step velocity
twist.angular.x = B_scale_*joy->axes[RB_];
twist.angular.y = B_scale_*joy->axes[LB_];
// Switch Trigger: Button A
switch_trigger = joy->buttons[sw_];
// ESTOP: Button B
ESTOP = joy->buttons[es_];
// Arrow Pad
updown = joy->axes[UD_];
leftright = -joy->axes[LR_];
// Top Bumpers
left_bump = joy->buttons[LT_];
right_bump = joy->buttons[RT_];
}
geometry_msgs::Twist Teleop::return_twist()
{
return twist;
}
bool Teleop::return_trigger()
{
return switch_trigger;
}
bool Teleop::return_estop()
{
return ESTOP;
}
mini_ros::JoyButtons Teleop::return_buttons()
{
mini_ros::JoyButtons jb;
jb.updown = updown;
jb.leftright = leftright;
jb.left_bump = left_bump;
jb.right_bump = right_bump;
return jb;
}
}
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""
DESCRIPTION:
SUBSCRIBERS:
"""
from __future__ import division
import rospy
from mini_ros.msg import IMUdata, ContactData
from std_msgs.msg import String
# Used for str(Boolean) --> Boolean
from distutils.util import strtobool
import sys
import rospkg
rospack = rospkg.RosPack()
sys.path.append(rospack.get_path('mini_ros') + '/../')
sys.path.append('../../')
from spot_real.Control.RPi.lib.Teensy_Interface import TeensyInterface
from spot_real.Control.RPi.lib.imu import IMU
class SensorInterface():
def __init__(self):
rospy.init_node('SensorInterface', anonymous=True)
self.TI = TeensyInterface()
self.imu_pub = rospy.Publisher('spot/imu', IMUdata, queue_size=1)
self.cnt_pub = rospy.Publisher('spot/contact',
ContactData,
queue_size=1)
self.str_pub = rospy.Publisher('spot/teensydebug',
String,
queue_size=1)
print("PUT SPOT ON THE GROUND, CALIBRATING IMU")
self.imu = IMU()
def read_sensors(self):
""" Reads IMU and Contact Sensor data from Teensy 4.0
and publishes to respective topics
"""
while not rospy.is_shutdown():
data = self.TI.read_buffer()
rospy.logdebug(data)
imu_dat = IMUdata()
imu_read = False
cnt_dat = ContactData()
cnt_read = False
msg = data.decode().split(",")
# REMOVE NEWLINE
msg[-1] = msg[-1].rstrip('\r\n')
# IMU
if msg[0] == "IMUDATA":
try:
imu_msg = [float(x) for x in msg[1:]]
# rospy.loginfo(imu_msg)
# IMU
imu_dat.roll = imu_msg[0]
imu_dat.pitch = imu_msg[1]
imu_dat.acc_x = imu_msg[2]
imu_dat.acc_y = imu_msg[3]
imu_dat.acc_z = imu_msg[4]
imu_dat.gyro_x = imu_msg[5]
imu_dat.gyro_y = imu_msg[6]
imu_dat.gyro_z = imu_msg[7]
imu_read = True
except:
rospy.logdebug("bad imu read")
# CONTACT
elif msg[0] == "CONTACT":
try:
cnt_msg = [strtobool(x) for x in msg[1:]]
# rospy.loginfo(cnt_msg)
# Contact Sensor
cnt_dat.FL = cnt_msg[0]
cnt_dat.FR = cnt_msg[1]
cnt_dat.BL = cnt_msg[2]
cnt_dat.BR = cnt_msg[3]
cnt_read = True
except:
rospy.logdebug("bad contact read")
# READ IMU
self.imu.filter_rpy()
imu_dat.roll = self.imu.true_roll
imu_dat.pitch = self.imu.true_pitch
imu_dat.acc_x = self.imu.imu_data[3]
imu_dat.acc_y = self.imu.imu_data[4]
imu_dat.acc_z = self.imu.imu_data[5]
imu_dat.gyro_x = self.imu.imu_data[0]
imu_dat.gyro_y = self.imu.imu_data[1]
imu_dat.gyro_z = self.imu.imu_data[2]
imu_read = True
if imu_read:
self.imu_pub.publish(imu_dat)
# rospy.logdebug("IMU")
if cnt_read:
self.cnt_pub.publish(cnt_dat)
# rospy.logdebug("CONTACT")
string_msg = String()
string_msg.data = data
self.str_pub.publish(string_msg)
def main():
""" The main() function. """
si = SensorInterface()
rospy.loginfo("Ready To Log Data!")
# rate = rospy.Rate(1.0 / 60.0)
while not rospy.is_shutdown():
si.read_sensors()
# rate.sleep()
rospy.spin()
if __name__ == '__main__':
try:
main()
except rospy.ROSInterruptException:
pass
@@ -0,0 +1,66 @@
#!/usr/bin/env python
"""
DESCRIPTION:
SUBSCRIBERS:
"""
from __future__ import division
import rospy
from mini_ros.srv import CalibServo, CalibServoResponse
from mini_ros.msg import JointPulse
import numpy as np
import sys
import rospkg
rospack = rospkg.RosPack()
sys.path.append(rospack.get_path('mini_ros') + '/../')
sys.path.append('../../')
class ServoCalibrator():
def __init__(self):
rospy.init_node('ServoCalibrator', anonymous=True)
self.serv = rospy.Service('servo_calibrator', CalibServo,
self.calib_service_cb)
self.jp_pub = rospy.Publisher('spot/pulse', JointPulse, queue_size=1)
def calib_service_cb(self, req):
""" Requests a servo to be moved to a certain position
Args: req
Returns: response
"""
try:
jp_msg = JointPulse()
jp_msg.servo_num = req.servo_num
jp_msg.servo_pulse = req.servo_pulse
self.jp_pub.publish(jp_msg)
response = "Servo Command Sent."
except rospy.ROSInterruptException:
response = "FAILED to send Servo Command"
return CalibServoResponse(response)
def main():
""" The main() function. """
srv_calib = ServoCalibrator()
rospy.loginfo(
"Use The servo_calibrator service (Pulse Width Unit is us (nominal ~500-2500))."
)
while not rospy.is_shutdown():
rospy.spin()
if __name__ == '__main__':
try:
main()
except rospy.ROSInterruptException:
pass
@@ -0,0 +1,256 @@
#!/usr/bin/env python
"""
DESCRIPTION:
SUBSCRIBERS:
"""
from __future__ import division
import os
import rospy
import numpy as np
from mini_ros.msg import MiniCmd, JoyButtons
import copy
import sys
import rospkg
rospack = rospkg.RosPack()
sys.path.append(rospack.get_path('mini_ros') + '/../')
sys.path.append('../../')
from spotmicro.GymEnvs.spot_bezier_env import spotBezierEnv
from spotmicro.Kinematics.SpotKinematics import SpotModel
from spotmicro.GaitGenerator.Bezier import BezierGait
# Controller Params
STEPLENGTH_SCALE = 0.06
Z_SCALE_CTRL = 0.12
RPY_SCALE = 0.6
SV_SCALE = 0.1
CHPD_SCALE = 0.0005
YAW_SCALE = 1.5
# AGENT PARAMS
CD_SCALE = 0.05
SLV_SCALE = 0.05
RESIDUALS_SCALE = 0.03
Z_SCALE = 0.05
# Filter actions
alpha = 0.7
# Added this to avoid filtering residuals
# -1 for all
class SpotCommander():
def __init__(self):
rospy.init_node('Policies', anonymous=True)
self.agents = {}
# self.movetypes = [
# "Forward", "Backward", "Left", "Right", "CW", "CCW", "Stop"
# ]
self.movetypes = ["Stop"]
self.mini_cmd = MiniCmd()
self.jb = JoyButtons()
self.mini_cmd.x_velocity = 0.0
self.mini_cmd.y_velocity = 0.0
self.mini_cmd.rate = 0.0
self.mini_cmd.roll = 0.0
self.mini_cmd.pitch = 0.0
self.mini_cmd.yaw = 0.0
self.mini_cmd.z = 0.0
self.mini_cmd.motion = "Stop"
self.mini_cmd.movement = "Stepping"
# FIXED
self.BaseStepVelocity = 0.1
self.StepVelocity = self.BaseStepVelocity
# Stock, use Bumpers to change
self.BaseSwingPeriod = 0.2
self.SwingPeriod = self.BaseSwingPeriod
# Stock, use arrow pads to change
self.BaseClearanceHeight = 0.04
self.BasePenetrationDepth = 0.005
self.ClearanceHeight = self.BaseClearanceHeight
self.PenetrationDepth = self.BasePenetrationDepth
self.load_spot()
# mini_cmd_cb from mini_cmd topic
self.sub_cmd = rospy.Subscriber('mini_cmd', MiniCmd, self.mini_cmd_cb)
self.sub_jb = rospy.Subscriber('joybuttons', JoyButtons, self.jb_cb)
self.time = rospy.get_time()
print("READY TO GO!")
def load_spot(self):
self.env = spotBezierEnv(render=True,
on_rack=False,
height_field=False,
draw_foot_path=False)
self.env.reset()
seed = 0
# Set seeds
self.env.seed(seed)
np.random.seed(seed)
state_dim = self.env.observation_space.shape[0]
print("STATE DIM: {}".format(state_dim))
action_dim = self.env.action_space.shape[0]
print("ACTION DIM: {}".format(action_dim))
max_action = float(self.env.action_space.high[0])
print("RECORDED MAX ACTION: {}".format(max_action))
self.state = self.env.reset()
# Load Spot Model
self.spot = SpotModel()
self.dt = self.env._time_step
self.T_bf0 = self.spot.WorldToFoot
self.T_bf = copy.deepcopy(self.T_bf0)
self.bzg = BezierGait(dt=self.env._time_step)
def mini_cmd_cb(self, mini_cmd):
""" Reads the desired Minitaur command and passes it for execution
Args: mini_cmd
"""
try:
# Update mini_cmd
self.mini_cmd = mini_cmd
# log input data as debug-level message
rospy.logdebug(mini_cmd)
except rospy.ROSInterruptException:
pass
def jb_cb(self, jb):
""" Reads the desired additional joystick buttons
Args: jb
"""
try:
# Update jb
self.jb = jb
# log input data as debug-level message
rospy.logdebug(jb)
except rospy.ROSInterruptException:
pass
def move(self):
""" Turn joystick inputs into commands
"""
if self.mini_cmd.motion != "Stop":
self.StepVelocity = self.BaseStepVelocity
self.SwingPeriod = np.clip(
self.BaseSwingPeriod +
(-self.mini_cmd.faster + -self.mini_cmd.slower) * SV_SCALE,
0.1, 0.3)
if self.mini_cmd.movement == "Stepping":
StepLength = self.mini_cmd.x_velocity + abs(
self.mini_cmd.y_velocity * 0.66)
StepLength = np.clip(StepLength, -1.0, 1.0)
StepLength *= STEPLENGTH_SCALE
LateralFraction = self.mini_cmd.y_velocity * np.pi / 2
YawRate = self.mini_cmd.rate * YAW_SCALE
# x offset
pos = np.array(
[0.0, 0.0, self.mini_cmd.z * Z_SCALE_CTRL])
orn = np.array([0.0, 0.0, 0.0])
else:
StepLength = 0.0
LateralFraction = 0.0
YawRate = 0.0
# RESET
self.ClearanceHeight = self.BaseClearanceHeight
self.PenetrationDepth = self.BasePenetrationDepth
self.StepVelocity = self.BaseStepVelocity
# x offset
pos = np.array(
[0.0, 0.0, self.mini_cmd.z * Z_SCALE_CTRL])
orn = np.array([
self.mini_cmd.roll * RPY_SCALE,
self.mini_cmd.pitch * RPY_SCALE,
self.mini_cmd.yaw * RPY_SCALE
])
else:
StepLength = 0.0
LateralFraction = 0.0
YawRate = 0.0
# RESET
self.ClearanceHeight = self.BaseClearanceHeight
self.PenetrationDepth = self.BasePenetrationDepth
self.StepVelocity = self.BaseStepVelocity
self.SwingPeriod = self.BaseSwingPeriod
pos = np.array([0.0, 0.0, 0.0])
orn = np.array([0.0, 0.0, 0.0])
# TODO: integrate into controller
self.ClearanceHeight += self.jb.updown * CHPD_SCALE
self.PenetrationDepth += self.jb.leftright * CHPD_SCALE
# Manual Reset
if self.jb.left_bump or self.jb.right_bump:
self.ClearanceHeight = self.BaseClearanceHeight
self.PenetrationDepth = self.BasePenetrationDepth
self.StepVelocity = self.BaseStepVelocity
self.SwingPeriod = self.BaseSwingPeriod
self.env.reset()
# print("SL: {} \tSV: {} \nLAT: {} \tYAW: {}".format(
# StepLength, self.StepVelocity, LateralFraction, YawRate))
# print("BASE VEL: {}".format(self.BaseStepVelocity))
# print("---------------------------------------")
contacts = self.state[-4:]
# Time
dt = rospy.get_time() - self.time
# print("dt: {}".format(dt))
self.time = rospy.get_time()
# Update Step Period
self.bzg.Tswing = self.SwingPeriod
self.T_bf = self.bzg.GenerateTrajectory(StepLength, LateralFraction,
YawRate, self.StepVelocity,
self.T_bf0, self.T_bf,
self.ClearanceHeight,
self.PenetrationDepth,
contacts, dt)
joint_angles = self.spot.IK(orn, pos, self.T_bf)
self.env.pass_joint_angles(joint_angles.reshape(-1))
# Get External Observations
# TODO
# self.env.spot.GetExternalObservations(bzg, bz_step)
# Step
action = self.env.action_space.sample()
action[:] = 0.0
self.state, reward, done, _ = self.env.step(action)
def main():
""" The main() function. """
mini_commander = SpotCommander()
rate = rospy.Rate(600.0)
while not rospy.is_shutdown():
# This is called continuously. Has timeout functionality too
mini_commander.move()
rate.sleep()
# rospy.spin()
if __name__ == '__main__':
try:
main()
except rospy.ROSInterruptException:
pass
@@ -0,0 +1,388 @@
#!/usr/bin/env python
"""
DESCRIPTION:
SUBSCRIBERS:
"""
from __future__ import division
import rospy
import numpy as np
from mini_ros.msg import MiniCmd, JoyButtons, IMUdata, ContactData, AgentData, JointAngles
import copy
import sys
import os
import rospkg
rospack = rospkg.RosPack()
sys.path.append(rospack.get_path('mini_ros') + '/../')
sys.path.append('../../')
from spotmicro.Kinematics.SpotKinematics import SpotModel
from spotmicro.GaitGenerator.Bezier import BezierGait
from spot_bullet.src.ars_lib.ars import ARSAgent, Normalizer, Policy
from spotmicro.GymEnvs.spot_bezier_env import spotBezierEnv
# Initialize Node
rospy.init_node('Policies', anonymous=True)
# Controller Params
STEPLENGTH_SCALE = rospy.get_param("STEPLENGTH_SCALE")
Z_SCALE_CTRL = rospy.get_param("Z_SCALE_CTRL")
RPY_SCALE = rospy.get_param("RPY_SCALE")
SV_SCALE = rospy.get_param("SV_SCALE")
CHPD_SCALE = rospy.get_param("CHPD_SCALE")
YAW_SCALE = rospy.get_param("YAW_SCALE")
# AGENT PARAMS
CD_SCALE = rospy.get_param("CD_SCALE")
SLV_SCALE = rospy.get_param("SLV_SCALE")
RESIDUALS_SCALE = rospy.get_param("RESIDUALS_SCALE")
Z_SCALE = rospy.get_param("Z_SCALE")
# Filter actions
alpha = rospy.get_param("alpha")
# Added this to avoid filtering residuals
# -1 for all
actions_to_filter = rospy.get_param("actions_to_filter")
class SpotCommander():
def __init__(self, Agent=True, contacts=False):
self.Agent = Agent
self.agent_num = rospy.get_param("agent_num")
self.movetypes = ["Stop"]
self.mini_cmd = MiniCmd()
self.jb = JoyButtons()
self.mini_cmd.x_velocity = 0.0
self.mini_cmd.y_velocity = 0.0
self.mini_cmd.rate = 0.0
self.mini_cmd.roll = 0.0
self.mini_cmd.pitch = 0.0
self.mini_cmd.yaw = 0.0
self.mini_cmd.z = 0.0
self.mini_cmd.motion = "Stop"
self.mini_cmd.movement = "Stepping"
# FIXED
self.BaseStepVelocity = rospy.get_param("BaseStepVelocity")
self.StepVelocity = copy.deepcopy(self.BaseStepVelocity)
# Stock, use Bumpers to change
self.BaseSwingPeriod = rospy.get_param("Tswing")
self.SwingPeriod = copy.deepcopy(self.BaseSwingPeriod)
self.SwingPeriod_LIMITS = rospy.get_param("SwingPeriod_LIMITS")
# Stock, use arrow pads to change
self.BaseClearanceHeight = rospy.get_param("BaseClearanceHeight")
self.BasePenetrationDepth = rospy.get_param("BasePenetrationDepth")
self.ClearanceHeight = copy.deepcopy(self.BaseClearanceHeight)
self.PenetrationDepth = copy.deepcopy(self.BasePenetrationDepth)
self.ClearanceHeight_LIMITS = rospy.get_param("ClearanceHeight_LIMITS")
self.PenetrationDepth_LIMITS = rospy.get_param(
"PenetrationDepth_LIMITS")
# Time
self.time = rospy.get_time()
self.enable_contact = contacts
# Contacts: FL, FR, BL, BR
self.contacts = [0, 0, 0, 0]
# IMU: R, P, Ax, Ay, Az, Gx, Gy, Gz
self.imu = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
# See spot_params.yaml in config
self.spot = SpotModel(
shoulder_length=rospy.get_param("shoulder_length"),
elbow_length=rospy.get_param("elbow_length"),
wrist_length=rospy.get_param("wrist_length"),
hip_x=rospy.get_param("hip_x"),
hip_y=rospy.get_param("hip_y"),
foot_x=rospy.get_param("foot_x"),
foot_y=rospy.get_param("foot_y"),
height=rospy.get_param("height"),
com_offset=rospy.get_param("com_offset"))
self.T_bf0 = self.spot.WorldToFoot
self.T_bf = copy.deepcopy(self.T_bf0)
# See spot_params.yaml in config
self.bzg = BezierGait(dt=rospy.get_param("dt"),
Tswing=rospy.get_param("Tswing"))
if self.Agent:
self.load_spot(contacts, agent_num=self.agent_num)
# cmd_cb from mini_cmd topic
self.sub_cmd = rospy.Subscriber('mini_cmd',
MiniCmd,
self.cmd_cb,
queue_size=1)
self.sub_jb = rospy.Subscriber('joybuttons',
JoyButtons,
self.jb_cb,
queue_size=1)
self.sub_imu = rospy.Subscriber('spot/imu',
IMUdata,
self.imu_cb,
queue_size=1)
self.sub_cnt = rospy.Subscriber('spot/contact',
ContactData,
self.cnt_cb,
queue_size=1)
self.ag_pub = rospy.Publisher('spot/agent', AgentData, queue_size=1)
self.ja_pub = rospy.Publisher('spot/joints', JointAngles, queue_size=1)
print("READY TO GO!")
def load_spot(self, contacts, state_dim=12, action_dim=14, agent_num=0):
self.policy = Policy(state_dim=state_dim, action_dim=action_dim)
self.normalizer = Normalizer(state_dim=state_dim)
env = spotBezierEnv(render=False,
on_rack=False,
height_field=False,
draw_foot_path=False)
agent = ARSAgent(self.normalizer, self.policy, env)
my_path = os.path.abspath(os.path.dirname(__file__))
if contacts:
models_path = os.path.join(my_path,
"../../spot_bullet/models/contact")
else:
models_path = os.path.join(my_path,
"../../spot_bullet/models/no_contact")
print("MODEL PATH: {}".format(my_path))
file_name = "spot_ars_"
if os.path.exists(models_path + "/" + file_name + str(agent_num) +
"_policy"):
print("Loading Existing agent: {}".format(agent_num))
agent.load(models_path + "/" + file_name + str(agent_num))
agent.policy.episode_steps = np.inf
self.policy = agent.policy
self.action = np.zeros(action_dim)
self.old_act = self.action[:actions_to_filter]
def imu_cb(self, imu):
""" Reads the IMU
Args: imu
"""
try:
# Update imu
self.imu = [
imu.roll, imu.pitch,
np.radians(imu.gyro_x),
np.radians(imu.gyro_y),
np.radians(imu.gyro_z), imu.acc_x, imu.acc_y, imu.acc_z - 9.81
]
# log input data as debug-level message
rospy.logdebug(imu)
except rospy.ROSInterruptException:
pass
def cnt_cb(self, cnt):
""" Reads the Contact Sensors
Args: cnt
"""
try:
# Update contacts
self.contacts = [cnt.FL, cnt.FR, cnt.BL, cnt.BR]
# log input data as debug-level message
rospy.logdebug(cnt)
except rospy.ROSInterruptException:
pass
def cmd_cb(self, mini_cmd):
""" Reads the desired Minitaur command and passes it for execution
Args: mini_cmd
"""
try:
# Update mini_cmd
self.mini_cmd = mini_cmd
# log input data as debug-level message
rospy.logdebug(mini_cmd)
except rospy.ROSInterruptException:
pass
def jb_cb(self, jb):
""" Reads the desired additional joystick buttons
Args: jb
"""
try:
# Update jb
self.jb = jb
# log input data as debug-level message
rospy.logdebug(jb)
except rospy.ROSInterruptException:
pass
def move(self):
""" Turn joystick inputs into commands
"""
# Move Type
if self.mini_cmd.movement == "Stepping":
step_or_view = False
else:
step_or_view = True
if self.mini_cmd.motion != "Stop":
self.StepVelocity = copy.deepcopy(self.BaseStepVelocity)
self.SwingPeriod = np.clip(
copy.deepcopy(self.BaseSwingPeriod) +
(-self.mini_cmd.faster + -self.mini_cmd.slower) * SV_SCALE,
self.SwingPeriod_LIMITS[0], self.SwingPeriod_LIMITS[1])
if self.mini_cmd.movement == "Stepping":
# Only take 2/3 of lateral step length or it will be too great.
StepLength = self.mini_cmd.x_velocity + abs(
self.mini_cmd.y_velocity * 0.66)
StepLength = np.clip(StepLength, -1.0, 1.0)
StepLength *= STEPLENGTH_SCALE
# Convert lateral joystick action into poolar coordinates
# for lateral movement
LateralFraction = self.mini_cmd.y_velocity * np.pi / 2
YawRate = self.mini_cmd.rate * YAW_SCALE
# NOTE: NO HEIGHT MOD DURING WALK
pos = np.array([0.0, 0.0, 0.0])
orn = np.array([0.0, 0.0, 0.0])
else:
StepLength = 0.0
LateralFraction = 0.0
YawRate = 0.0
# RESET
self.ClearanceHeight = copy.deepcopy(self.BaseClearanceHeight)
self.PenetrationDepth = copy.deepcopy(
self.BasePenetrationDepth)
self.StepVelocity = copy.deepcopy(self.BaseStepVelocity)
# x offset
pos = np.array([0.0, 0.0, self.mini_cmd.z * Z_SCALE_CTRL])
orn = np.array([
self.mini_cmd.roll * RPY_SCALE,
self.mini_cmd.pitch * RPY_SCALE,
self.mini_cmd.yaw * RPY_SCALE
])
else:
StepLength = 0.0
LateralFraction = 0.0
YawRate = 0.0
# RESET
self.ClearanceHeight = self.BaseClearanceHeight
self.PenetrationDepth = self.BasePenetrationDepth
self.StepVelocity = self.BaseStepVelocity
self.SwingPeriod = self.BaseSwingPeriod
pos = np.array([0.0, 0.0, 0.0])
orn = np.array([0.0, 0.0, 0.0])
# TODO: integrate into controller
self.ClearanceHeight += self.jb.updown * CHPD_SCALE
self.PenetrationDepth += self.jb.leftright * CHPD_SCALE
# Manual Reset
if self.jb.left_bump or self.jb.right_bump:
self.ClearanceHeight = copy.deepcopy(self.BaseClearanceHeight)
self.PenetrationDepth = copy.deepcopy(self.BasePenetrationDepth)
self.StepVelocity = copy.deepcopy(self.BaseStepVelocity)
self.SwingPeriod = copy.deepcopy(self.BaseSwingPeriod)
# OPTIONAL: Agent
if self.Agent and self.mini_cmd.motion != "Stop":
phases = copy.deepcopy(self.bzg.Phases)
# Total 12
state = []
# r, p, gz, gy, gz, ax, ay, az (8)
state.extend(self.imu)
# FL, FR, BL, BR (4)
state.extend(phases)
# FL, FR, BL, BR (4)
if self.enable_contact:
state.extend(self.contacts)
self.normalizer.observe(state)
# Don't normalize contacts
state[:-4] = self.normalizer.normalize(state)[:-4]
self.action = self.policy.evaluate(state, None, None)
self.action = np.tanh(self.action)
# EXP FILTER
self.action[:actions_to_filter] = alpha * self.old_act + (
1.0 - alpha) * self.action[:actions_to_filter]
self.old_act = self.action[:actions_to_filter]
self.ClearanceHeight += self.action[0] * CD_SCALE
# Time
dt = rospy.get_time() - self.time
# print("dt: {}".format(dt))
self.time = rospy.get_time()
# Update Step Period
self.bzg.Tswing = self.SwingPeriod
# CLIP
self.ClearanceHeight = np.clip(self.ClearanceHeight,
self.ClearanceHeight_LIMITS[0],
self.ClearanceHeight_LIMITS[1])
self.PenetrationDepth = np.clip(self.PenetrationDepth,
self.PenetrationDepth_LIMITS[0],
self.PenetrationDepth_LIMITS[1])
self.T_bf = self.bzg.GenerateTrajectory(StepLength, LateralFraction,
YawRate, self.StepVelocity,
self.T_bf0, self.T_bf,
self.ClearanceHeight,
self.PenetrationDepth,
self.contacts, dt)
T_bf_copy = copy.deepcopy(self.T_bf)
# OPTIONAL: Agent
if self.Agent and self.mini_cmd.motion != "Stop":
self.action[2:] *= RESIDUALS_SCALE
# Add DELTA to XYZ Foot Poses
T_bf_copy["FL"][:3, 3] += self.action[2:5]
T_bf_copy["FR"][:3, 3] += self.action[5:8]
T_bf_copy["BL"][:3, 3] += self.action[8:11]
T_bf_copy["BR"][:3, 3] += self.action[11:14]
pos[2] += abs(self.action[1]) * Z_SCALE
joint_angles = self.spot.IK(orn, pos, T_bf_copy)
ja_msg = JointAngles()
ja_msg.fls = np.degrees(joint_angles[0][0])
ja_msg.fle = np.degrees(joint_angles[0][1])
ja_msg.flw = np.degrees(joint_angles[0][2])
ja_msg.frs = np.degrees(joint_angles[1][0])
ja_msg.fre = np.degrees(joint_angles[1][1])
ja_msg.frw = np.degrees(joint_angles[1][2])
ja_msg.bls = np.degrees(joint_angles[2][0])
ja_msg.ble = np.degrees(joint_angles[2][1])
ja_msg.blw = np.degrees(joint_angles[2][2])
ja_msg.brs = np.degrees(joint_angles[3][0])
ja_msg.bre = np.degrees(joint_angles[3][1])
ja_msg.brw = np.degrees(joint_angles[3][2])
# Move Type
ja_msg.step_or_view = step_or_view
self.ja_pub.publish(ja_msg)
def main():
""" The main() function. """
mini_commander = SpotCommander()
rate = rospy.Rate(600.0)
while not rospy.is_shutdown():
# This is called continuously. Has timeout functionality too
mini_commander.move()
rate.sleep()
# rospy.spin()
if __name__ == '__main__':
try:
main()
except rospy.ROSInterruptException:
pass
+184
View File
@@ -0,0 +1,184 @@
/// \file
/// \brief Draws Each Obstacle in RViz using MarkerArrays
///
/// PARAMETERS:
/// PUBLISHES:
/// SUBSCRIBES:
/// FUNCTIONS:
#include <ros/ros.h>
#include <math.h>
#include <string>
#include <vector>
#include <mini_ros/spot.hpp>
#include <mini_ros/teleop.hpp>
#include "mini_ros/MiniCmd.h"
#include "std_srvs/Empty.h"
#include "std_msgs/Bool.h"
// Global Vars
spot::Spot spot_mini = spot::Spot();
bool teleop_flag = false;
bool motion_flag = false;
bool ESTOP = false;
// Init Time
ros::Time current_time;
ros::Time last_time;
void teleop_callback(const geometry_msgs::Twist &tw)
{
/// \brief cmd_vel subscriber callback. Records commanded twist
///
/// \param tw (geometry_msgs::Twist): the commanded linear and angular velocity
/**
* This function runs every time we get a geometry_msgs::Twist message on the "cmd_vel" topic.
* We generally use the const <message>ConstPtr &msg syntax to prevent our node from accidentally
* changing the message, in the case that another node is also listening to it.
*/
spot_mini.update_command(tw.linear.x, tw.linear.y, tw.linear.z, tw.angular.z, tw.angular.x, tw.angular.y);
}
void estop_callback(const std_msgs::Bool &estop)
{
if (estop.data)
{
spot_mini.update_command(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
motion_flag = true;
if (!ESTOP)
{
ROS_ERROR("ENGAGING MANUAL E-STOP!");
ESTOP = true;
} else
{
ROS_WARN("DIS-ENGAGING MANUAL E-STOP!");
ESTOP = false;
}
}
last_time = ros::Time::now();
}
bool swm_callback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
/// Switches the Movement mode from FB (Forward/Backward) to LR (Left/Right)
/// and vice versa
{
spot_mini.switch_movement();
motion_flag = true;
return true;
}
int main(int argc, char** argv)
/// The Main Function ///
{
ROS_INFO("STARTING NODE: spot_mini State Machine");
// Vars
double frequency = 5;
// Seconds for timeout
double timeout = 1.0;
ros::init(argc, argv, "mini_sm_node"); // register the node on ROS
ros::NodeHandle nh; // get a handle to ROS
ros::NodeHandle nh_("~"); // get a handle to ROS
// Parameters
nh_.getParam("frequency", frequency);
// Init Subscriber
ros::Subscriber teleop_sub = nh.subscribe("teleop", 1, teleop_callback);
ros::Subscriber estop_sub = nh.subscribe("estop", 1, estop_callback);
// Init Command Publisher
ros::Publisher mini_pub = nh.advertise<mini_ros::MiniCmd>("mini_cmd", 1);
// Init Switch Movement Service Server
ros::ServiceServer switch_movement_server = nh.advertiseService("switch_movement", swm_callback);
// Init MiniCmd
mini_ros::MiniCmd mini_cmd;
// Placeholder
mini_cmd.x_velocity = 0.0;
mini_cmd.y_velocity = 0.0;
mini_cmd.rate = 0.0;
mini_cmd.roll = 0.0;
mini_cmd.pitch = 0.0;
mini_cmd.yaw = 0.0;
mini_cmd.z = 0.0;
mini_cmd.faster = 0.0;
mini_cmd.slower = 0.0;
mini_cmd.motion = "Stop";
mini_cmd.movement = "Stepping";
ros::Rate rate(frequency);
current_time = ros::Time::now();
last_time = ros::Time::now();
// Main While
while (ros::ok())
{
ros::spinOnce();
current_time = ros::Time::now();
spot::SpotCommand cmd = spot_mini.return_command();
// Condition for sending non-stop command
if (!motion_flag and !(current_time.toSec() - last_time.toSec() > timeout) and !ESTOP)
{
mini_cmd.x_velocity = cmd.x_velocity;
mini_cmd.y_velocity = cmd.y_velocity;
mini_cmd.rate = cmd.rate;
mini_cmd.roll = cmd.roll;
mini_cmd.pitch = cmd.pitch;
mini_cmd.yaw = cmd.yaw;
mini_cmd.z = cmd.z;
mini_cmd.faster = cmd.faster;
mini_cmd.slower = cmd.slower;
// Now convert enum to string
// Motion
if (cmd.motion == spot::Go)
{
mini_cmd.motion = "Go";
} else
{
mini_cmd.motion = "Stop";
}
// Movement
if (cmd.movement == spot::Stepping)
{
mini_cmd.movement = "Stepping";
} else
{
mini_cmd.movement = "Viewing";
}
} else
{
mini_cmd.x_velocity = 0.0;
mini_cmd.y_velocity = 0.0;
mini_cmd.rate = 0.0;
mini_cmd.roll = 0.0;
mini_cmd.pitch = 0.0;
mini_cmd.yaw = 0.0;
mini_cmd.z = 0.0;
mini_cmd.faster = 0.0;
mini_cmd.slower = 0.0;
mini_cmd.motion = "Stop";
}
if (current_time.toSec() - last_time.toSec() > timeout)
{
ROS_ERROR("TIMEOUT...ENGAGING E-STOP!");
}
// Now publish
mini_pub.publish(mini_cmd);
motion_flag = false;
rate.sleep();
}
return 0;
}
+134
View File
@@ -0,0 +1,134 @@
/// \file
/// \brief Draws Each Obstacle in RViz using MarkerArrays
///
/// PARAMETERS:
/// PUBLISHES:
/// SUBSCRIBES:
/// FUNCTIONS:
#include <ros/ros.h>
#include <math.h>
#include <string>
#include <vector>
#include <mini_ros/spot.hpp>
#include <mini_ros/teleop.hpp>
#include "mini_ros/MiniCmd.h"
#include "std_msgs/Bool.h"
#include "std_srvs/Empty.h"
#include "mini_ros/JoyButtons.h"
#include <functional> // To use std::bind
int main(int argc, char** argv)
/// The Main Function ///
{
ROS_INFO("STARTING NODE: Teleoperation");
// Vars
double frequency = 60;
int linear_x = 4;
int linear_y = 3;
int linear_z = 1;
int angular = 0;
int sw = 0;
int es = 1;
int RB = 5;
int LB = 2;
int RT = 5;
int LT = 4;
int UD = 7;
int LR = 6;
double l_scale = 1.0;
double a_scale = 1.0;
double B_scale = 1.0;
double debounce_thresh = 0.15; // sec
ros::init(argc, argv, "teleop_node"); // register the node on ROS
ros::NodeHandle nh; // get a handle to ROS
ros::NodeHandle nh_("~"); // get a handle to ROS
// Parameters
nh_.getParam("frequency", frequency);
nh_.getParam("axis_linear_x", linear_x);
nh_.getParam("axis_linear_y", linear_y);
nh_.getParam("axis_linear_z", linear_z);
nh_.getParam("axis_angular", angular);
nh_.getParam("scale_linear", l_scale);
nh_.getParam("scale_angular", a_scale);
nh_.getParam("scale_bumper", B_scale);
nh_.getParam("button_switch", sw);
nh_.getParam("button_estop", es);
nh_.getParam("rb", RB);
nh_.getParam("lb", LB);
nh_.getParam("rt", RT);
nh_.getParam("lt", LT);
nh_.getParam("updown", UD);
nh_.getParam("leftright", LR);
nh_.getParam("debounce_thresh", debounce_thresh);
tele::Teleop teleop = tele::Teleop(linear_x, linear_y, linear_z, angular,
l_scale, a_scale, LB, RB, B_scale, LT,
RT, UD, LR, sw, es);
// Init Switch Movement Server
ros::ServiceClient switch_movement_client = nh.serviceClient<std_srvs::Empty>("switch_movement");
ros::service::waitForService("switch_movement", -1);
// Init ESTOP Publisher
ros::Publisher estop_pub = nh.advertise<std_msgs::Bool>("estop", 1);
// Init Command Publisher
ros::Publisher vel_pub = nh.advertise<geometry_msgs::Twist>("teleop", 1);
// Init Joy Button Publisher
ros::Publisher jb_pub = nh.advertise<mini_ros::JoyButtons>("joybuttons", 1);
// Init Subscriber (also handles pub)
// TODO: Figure out how to use std::bind properly
// ros::Subscriber joy_sub = nh.subscribe<sensor_msgs::Joy>("joy", 1, std::bind(&tele::Teleop::joyCallback, std::placeholders::_1, vel_pub), &teleop);
ros::Subscriber joy_sub = nh.subscribe<sensor_msgs::Joy>("joy", 1, &tele::Teleop::joyCallback, &teleop);
ros::Rate rate(frequency);
// Record time for debouncing buttons
ros::Time current_time = ros::Time::now();
ros::Time last_time = ros::Time::now();
// Main While
while (ros::ok())
{
ros::spinOnce();
current_time = ros::Time::now();
std_msgs::Bool estop;
estop.data = teleop.return_estop();
if (estop.data and current_time.toSec() - last_time.toSec() >= debounce_thresh)
{
ROS_INFO("SENDING E-STOP COMMAND!");
last_time = ros::Time::now();
} else if (!teleop.return_trigger())
{
// Send Twist
vel_pub.publish(teleop.return_twist());
estop.data = 0;
} else if (current_time.toSec() - last_time.toSec() >= debounce_thresh)
{
// Call Switch Service
std_srvs::Empty e;
switch_movement_client.call(e);
estop.data = 0;
last_time = ros::Time::now();
}
// pub buttons
jb_pub.publish(teleop.return_buttons());
estop_pub.publish(estop);
rate.sleep();
}
return 0;
}